From 6d2e2fbb65c4229f597f5b5faa9258c4f40170d8 Mon Sep 17 00:00:00 2001 From: Chandana Kishori Chiluveru Date: Sun, 4 Sep 2022 15:00:18 +0530 Subject: [PATCH 1/2] soc: qcom: Add SPS driver snapshot SPS (Smart Peripheral System) enables the support of all BAMs in the system which provide DMA functionality to various peripherals. This snapshot is taken from msm-5.15 kernel 'commit d809fcbdcfc3 ("Merge "msm-kernel: Enable ASM330 driver compilation"")'. Change-Id: I88d589b38f6ef27425b64699461655603f101fba Signed-off-by: Chandana Kishori Chiluveru --- drivers/soc/qcom/Kconfig | 29 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/sps/Makefile | 5 + drivers/soc/qcom/sps/bam.c | 2317 ++++++++++++++++++++++++ drivers/soc/qcom/sps/bam.h | 441 +++++ drivers/soc/qcom/sps/sps.c | 2938 +++++++++++++++++++++++++++++++ drivers/soc/qcom/sps/sps_bam.c | 2503 ++++++++++++++++++++++++++ drivers/soc/qcom/sps/sps_bam.h | 608 +++++++ drivers/soc/qcom/sps/sps_core.h | 95 + drivers/soc/qcom/sps/sps_dma.c | 921 ++++++++++ drivers/soc/qcom/sps/sps_map.c | 132 ++ drivers/soc/qcom/sps/sps_map.h | 39 + drivers/soc/qcom/sps/sps_mem.c | 164 ++ drivers/soc/qcom/sps/sps_rm.c | 870 +++++++++ drivers/soc/qcom/sps/spsi.h | 527 ++++++ include/linux/msm-sps.h | 1635 +++++++++++++++++ 16 files changed, 13225 insertions(+) create mode 100644 drivers/soc/qcom/sps/Makefile create mode 100644 drivers/soc/qcom/sps/bam.c create mode 100644 drivers/soc/qcom/sps/bam.h create mode 100644 drivers/soc/qcom/sps/sps.c create mode 100644 drivers/soc/qcom/sps/sps_bam.c create mode 100644 drivers/soc/qcom/sps/sps_bam.h create mode 100644 drivers/soc/qcom/sps/sps_core.h create mode 100644 drivers/soc/qcom/sps/sps_dma.c create mode 100644 drivers/soc/qcom/sps/sps_map.c create mode 100644 drivers/soc/qcom/sps/sps_map.h create mode 100644 drivers/soc/qcom/sps/sps_mem.c create mode 100644 drivers/soc/qcom/sps/sps_rm.c create mode 100644 drivers/soc/qcom/sps/spsi.h create mode 100644 include/linux/msm-sps.h diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a8c43ca4a6d7..a44202ebe2cd 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -4,6 +4,35 @@ # menu "Qualcomm SoC drivers" +config SPS + tristate "SPS support" + select GENERIC_ALLOCATOR + help + The SPS (Smart Peripheral Switch) is a DMA engine. + It can move data in the following modes: + 1. Peripheral-to-Peripheral. + 2. Peripheral-to-Memory. + 3. Memory-to-Memory. + +config SPS_SUPPORT_BAMDMA + bool "SPS supports BAM DMA" + depends on SPS + help + The BAM-DMA is used for Memory-to-Memory transfers. + The main use cases is RPC between processors. + The BAM-DMA hardware has 2 registers sets: + 1. A BAM HW like all the peripherals. + 2. A DMA channel configuration (i.e. channel priority). + +config SPS_SUPPORT_NDP_BAM + bool "SPS supports NDP BAM" + depends on SPS + help + No-Data-Path BAM is used to improve BAM performance. + NDP BAMs enables peripherals with fast fabric connectivity + to do the actual data transfer themselves, instead of the + BAM. + config QCOM_AOSS_QMP tristate "Qualcomm AOSS Driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index c324f0d4e817..31894502518e 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_QCOM_VA_MINIDUMP) += qcom_va_minidump.o obj-$(CONFIG_QCOM_LOGBUF_VENDOR_HOOKS) += qcom_logbuf_vh.o obj-$(CONFIG_QCOM_HUNG_TASK_ENH) += hung_task_enh.o obj-$(CONFIG_GH_TLMM_VM_MEM_ACCESS) += gh_tlmm_vm_mem_access.o +obj-$(CONFIG_SPS) += sps/ obj-$(CONFIG_QCOM_RAMDUMP) += qcom_ramdump.o obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o obj-$(CONFIG_QCOM_DCVS) += dcvs/ diff --git a/drivers/soc/qcom/sps/Makefile b/drivers/soc/qcom/sps/Makefile new file mode 100644 index 000000000000..7eb703c5edaa --- /dev/null +++ b/drivers/soc/qcom/sps/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Makefile for SPS BAM driver +# +obj-$(CONFIG_SPS) += sps_drv.o +sps_drv-objs := bam.o sps_bam.o sps.o sps_dma.o sps_map.o sps_mem.o sps_rm.o diff --git a/drivers/soc/qcom/sps/bam.c b/drivers/soc/qcom/sps/bam.c new file mode 100644 index 000000000000..1fa340a18eb1 --- /dev/null +++ b/drivers/soc/qcom/sps/bam.c @@ -0,0 +1,2317 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/* Bus-Access-Manager (BAM) Hardware manager. */ + +#include +#include +#include +#include +#include +#include + +#include "bam.h" +#include "sps_bam.h" + +/** + * Valid BAM Hardware version. + * + */ +#define BAM_MIN_VERSION 2 +#define BAM_MAX_VERSION 0x2f + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + +/* Maximum number of execution environment */ +#define BAM_MAX_EES 8 + +/** + * BAM Hardware registers bitmask. + * format: _ + * + */ +/* CTRL */ +#define BAM_MESS_ONLY_CANCEL_WB 0x100000 +#define CACHE_MISS_ERR_RESP_EN 0x80000 +#define LOCAL_CLK_GATING 0x60000 +#define IBC_DISABLE 0x10000 +#define BAM_CACHED_DESC_STORE 0x8000 +#define BAM_DESC_CACHE_SEL 0x6000 +#define BAM_EN_ACCUM 0x10 +#define BAM_EN 0x2 +#define BAM_SW_RST 0x1 + +/* REVISION */ +#define BAM_INACTIV_TMR_BASE 0xff000000 +#define BAM_CMD_DESC_EN 0x800000 +#define BAM_DESC_CACHE_DEPTH 0x600000 +#define BAM_NUM_INACTIV_TMRS 0x100000 +#define BAM_INACTIV_TMRS_EXST 0x80000 +#define BAM_HIGH_FREQUENCY_BAM 0x40000 +#define BAM_HAS_NO_BYPASS 0x20000 +#define BAM_SECURED 0x10000 +#define BAM_USE_VMIDMT 0x8000 +#define BAM_AXI_ACTIVE 0x4000 +#define BAM_CE_BUFFER_SIZE 0x3000 +#define BAM_NUM_EES 0xf00 +#define BAM_REVISION 0xff + +/* SW_REVISION */ +#define BAM_MAJOR 0xf0000000 +#define BAM_MINOR 0xfff0000 +#define BAM_STEP 0xffff + +/* NUM_PIPES */ +#define BAM_NON_PIPE_GRP 0xff000000 +#define BAM_PERIPH_NON_PIPE_GRP 0xff0000 +#define BAM_DATA_ADDR_BUS_WIDTH 0xC000 +#define BAM_NUM_PIPES 0xff + +/* TIMER */ +#define BAM_TIMER 0xffff + +/* TIMER_CTRL */ +#define TIMER_RST 0x80000000 +#define TIMER_RUN 0x40000000 +#define TIMER_MODE 0x20000000 +#define TIMER_TRSHLD 0xffff + +/* DESC_CNT_TRSHLD */ +#define BAM_DESC_CNT_TRSHLD 0xffff + +/* IRQ_SRCS */ +#define BAM_IRQ 0x80000000 +#define P_IRQ 0x7fffffff + +/* IRQ_STTS */ +#define IRQ_STTS_BAM_TIMER_IRQ 0x10 +#define IRQ_STTS_BAM_EMPTY_IRQ 0x8 +#define IRQ_STTS_BAM_ERROR_IRQ 0x4 +#define IRQ_STTS_BAM_HRESP_ERR_IRQ 0x2 + +/* IRQ_CLR */ +#define IRQ_CLR_BAM_TIMER_IRQ 0x10 +#define IRQ_CLR_BAM_EMPTY_CLR 0x8 +#define IRQ_CLR_BAM_ERROR_CLR 0x4 +#define IRQ_CLR_BAM_HRESP_ERR_CLR 0x2 + +/* IRQ_EN */ +#define IRQ_EN_BAM_TIMER_IRQ 0x10 +#define IRQ_EN_BAM_EMPTY_EN 0x8 +#define IRQ_EN_BAM_ERROR_EN 0x4 +#define IRQ_EN_BAM_HRESP_ERR_EN 0x2 + +/* AHB_MASTER_ERR_CTRLS */ +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HVMID 0x7c0000 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_DIRECT_MODE 0x20000 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HCID 0x1f000 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HPROT 0xf00 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HBURST 0xe0 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HSIZE 0x18 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HWRITE 0x4 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HTRANS 0x3 + +/* TRUST_REG */ +#define LOCK_EE_CTRL 0x2000 +#define BAM_VMID 0x1f00 +#define BAM_RST_BLOCK 0x80 +#define BAM_EE 0x7 + +/* TEST_BUS_SEL */ +#define BAM_SW_EVENTS_ZERO 0x200000 +#define BAM_SW_EVENTS_SEL 0x180000 +#define BAM_DATA_ERASE 0x40000 +#define BAM_DATA_FLUSH 0x20000 +#define BAM_CLK_ALWAYS_ON 0x10000 +#define BAM_TESTBUS_SEL 0x7f + +/* CNFG_BITS */ +#define CNFG_BITS_AOS_OVERFLOW_PRVNT 0x80000000 +#define CNFG_BITS_MULTIPLE_EVENTS_DESC_AVAIL_EN 0x40000000 +#define CNFG_BITS_MULTIPLE_EVENTS_SIZE_EN 0x20000000 +#define CNFG_BITS_BAM_ZLT_W_CD_SUPPORT 0x10000000 +#define CNFG_BITS_BAM_CD_ENABLE 0x8000000 +#define CNFG_BITS_BAM_AU_ACCUMED 0x4000000 +#define CNFG_BITS_BAM_PSM_P_HD_DATA 0x2000000 +#define CNFG_BITS_BAM_REG_P_EN 0x1000000 +#define CNFG_BITS_BAM_WB_DSC_AVL_P_RST 0x800000 +#define CNFG_BITS_BAM_WB_RETR_SVPNT 0x400000 +#define CNFG_BITS_BAM_WB_CSW_ACK_IDL 0x200000 +#define CNFG_BITS_BAM_WB_BLK_CSW 0x100000 +#define CNFG_BITS_BAM_WB_P_RES 0x80000 +#define CNFG_BITS_BAM_SI_P_RES 0x40000 +#define CNFG_BITS_BAM_AU_P_RES 0x20000 +#define CNFG_BITS_BAM_PSM_P_RES 0x10000 +#define CNFG_BITS_BAM_PSM_CSW_REQ 0x8000 +#define CNFG_BITS_BAM_SB_CLK_REQ 0x4000 +#define CNFG_BITS_BAM_IBC_DISABLE 0x2000 +#define CNFG_BITS_BAM_NO_EXT_P_RST 0x1000 +#define CNFG_BITS_BAM_FULL_PIPE 0x800 +#define CNFG_BITS_BAM_PIPE_CNFG 0x4 + +/* PIPE_ATTR_EEn*/ +#define BAM_ENABLED 0x80000000 +#define P_ATTR 0x7fffffff + +/* P_ctrln */ +#define P_LOCK_GROUP 0x1f0000 +#define P_WRITE_NWD 0x800 +#define P_PREFETCH_LIMIT 0x600 +#define P_AUTO_EOB_SEL 0x180 +#define P_AUTO_EOB 0x40 +#define P_SYS_MODE 0x20 +#define P_SYS_STRM 0x10 +#define P_DIRECTION 0x8 +#define P_EN 0x2 + +/* P_RSTn */ +#define P_RST_P_SW_RST 0x1 + +/* P_HALTn */ +#define P_HALT_P_PIPE_EMPTY 0x8 +#define P_HALT_P_LAST_DESC_ZLT 0x4 +#define P_HALT_P_PROD_HALTED 0x2 +#define P_HALT_P_HALT 0x1 + +/* P_TRUST_REGn */ +#define BAM_P_VMID 0x1f00 +#define BAM_P_SUP_GROUP 0xf8 +#define BAM_P_EE 0x7 + +/* P_IRQ_STTSn */ +#define P_IRQ_STTS_P_HRESP_ERR_IRQ 0x80 +#define P_IRQ_STTS_P_PIPE_RST_ERR_IRQ 0x40 +#define P_IRQ_STTS_P_TRNSFR_END_IRQ 0x20 +#define P_IRQ_STTS_P_ERR_IRQ 0x10 +#define P_IRQ_STTS_P_OUT_OF_DESC_IRQ 0x8 +#define P_IRQ_STTS_P_WAKE_IRQ 0x4 +#define P_IRQ_STTS_P_TIMER_IRQ 0x2 +#define P_IRQ_STTS_P_PRCSD_DESC_IRQ 0x1 + +/* P_IRQ_CLRn */ +#define P_IRQ_CLR_P_HRESP_ERR_CLR 0x80 +#define P_IRQ_CLR_P_PIPE_RST_ERR_CLR 0x40 +#define P_IRQ_CLR_P_TRNSFR_END_CLR 0x20 +#define P_IRQ_CLR_P_ERR_CLR 0x10 +#define P_IRQ_CLR_P_OUT_OF_DESC_CLR 0x8 +#define P_IRQ_CLR_P_WAKE_CLR 0x4 +#define P_IRQ_CLR_P_TIMER_CLR 0x2 +#define P_IRQ_CLR_P_PRCSD_DESC_CLR 0x1 + +/* P_IRQ_ENn */ +#define P_IRQ_EN_P_HRESP_ERR_EN 0x80 +#define P_IRQ_EN_P_PIPE_RST_ERR_EN 0x40 +#define P_IRQ_EN_P_TRNSFR_END_EN 0x20 +#define P_IRQ_EN_P_ERR_EN 0x10 +#define P_IRQ_EN_P_OUT_OF_DESC_EN 0x8 +#define P_IRQ_EN_P_WAKE_EN 0x4 +#define P_IRQ_EN_P_TIMER_EN 0x2 +#define P_IRQ_EN_P_PRCSD_DESC_EN 0x1 + +/* P_TIMERn */ +#define P_TIMER_P_TIMER 0xffff + +/* P_TIMER_ctrln */ +#define P_TIMER_RST 0x80000000 +#define P_TIMER_RUN 0x40000000 +#define P_TIMER_MODE 0x20000000 +#define P_TIMER_TRSHLD 0xffff + +/* P_PRDCR_SDBNDn */ +#define P_PRDCR_SDBNDn_BAM_P_SB_UPDATED 0x1000000 +#define P_PRDCR_SDBNDn_BAM_P_TOGGLE 0x100000 +#define P_PRDCR_SDBNDn_BAM_P_CTRL 0xf0000 +#define P_PRDCR_SDBNDn_BAM_P_BYTES_FREE 0xffff + +/* P_CNSMR_SDBNDn */ +#define P_CNSMR_SDBNDn_BAM_P_SB_UPDATED 0x1000000 +#define P_CNSMR_SDBNDn_BAM_P_WAIT_4_ACK 0x800000 +#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE 0x400000 +#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE_R 0x200000 +#define P_CNSMR_SDBNDn_BAM_P_TOGGLE 0x100000 +#define P_CNSMR_SDBNDn_BAM_P_CTRL 0xf0000 +#define P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL 0xffff + +/* P_EVNT_regn */ +#define P_BYTES_CONSUMED 0xffff0000 +#define P_DESC_FIFO_PEER_OFST 0xffff + +/* P_SW_ofstsn */ +#define SW_OFST_IN_DESC 0xffff0000 +#define SW_DESC_OFST 0xffff + +/* P_EVNT_GEN_TRSHLDn */ +#define P_EVNT_GEN_TRSHLD_P_TRSHLD 0xffff + +/* P_FIFO_sizesn */ +#define P_DATA_FIFO_SIZE 0xffff0000 +#define P_DESC_FIFO_SIZE 0xffff + +#define P_RETR_CNTXT_RETR_DESC_OFST 0xffff0000 +#define P_RETR_CNTXT_RETR_OFST_IN_DESC 0xffff +#define P_SI_CNTXT_SI_DESC_OFST 0xffff +#define P_DF_CNTXT_WB_ACCUMULATED 0xffff0000 +#define P_DF_CNTXT_DF_DESC_OFST 0xffff +#define P_AU_PSM_CNTXT_1_AU_PSM_ACCUMED 0xffff0000 +#define P_AU_PSM_CNTXT_1_AU_ACKED 0xffff +#define P_PSM_CNTXT_2_PSM_DESC_VALID 0x80000000 +#define P_PSM_CNTXT_2_PSM_DESC_IRQ 0x40000000 +#define P_PSM_CNTXT_2_PSM_DESC_IRQ_DONE 0x20000000 +#define P_PSM_CNTXT_2_PSM_GENERAL_BITS 0x1e000000 +#define P_PSM_CNTXT_2_PSM_CONS_STATE 0x1c00000 +#define P_PSM_CNTXT_2_PSM_PROD_SYS_STATE 0x380000 +#define P_PSM_CNTXT_2_PSM_PROD_B2B_STATE 0x70000 +#define P_PSM_CNTXT_2_PSM_DESC_SIZE 0xffff +#define P_PSM_CNTXT_4_PSM_DESC_OFST 0xffff0000 +#define P_PSM_CNTXT_4_PSM_SAVED_ACCUMED_SIZE 0xffff +#define P_PSM_CNTXT_5_PSM_BLOCK_BYTE_CNT 0xffff0000 +#define P_PSM_CNTXT_5_PSM_OFST_IN_DESC 0xffff + +#else + +/* Maximum number of execution environment */ +#define BAM_MAX_EES 4 + +/** + * BAM Hardware registers bitmask. + * format: _ + * + */ +/* CTRL */ +#define IBC_DISABLE 0x10000 +#define BAM_CACHED_DESC_STORE 0x8000 +#define BAM_DESC_CACHE_SEL 0x6000 +/* BAM_PERIPH_IRQ_SIC_SEL is an obsolete field; This bit is reserved now */ +#define BAM_PERIPH_IRQ_SIC_SEL 0x1000 +#define BAM_EN_ACCUM 0x10 +#define BAM_EN 0x2 +#define BAM_SW_RST 0x1 + +/* REVISION */ +#define BAM_INACTIV_TMR_BASE 0xff000000 +#define BAM_INACTIV_TMRS_EXST 0x80000 +#define BAM_HIGH_FREQUENCY_BAM 0x40000 +#define BAM_HAS_NO_BYPASS 0x20000 +#define BAM_SECURED 0x10000 +#define BAM_NUM_EES 0xf00 +#define BAM_REVISION 0xff + +/* NUM_PIPES */ +#define BAM_NON_PIPE_GRP 0xff000000 +#define BAM_PERIPH_NON_PIPE_GRP 0xff0000 +#define BAM_DATA_ADDR_BUS_WIDTH 0xC000 +#define BAM_NUM_PIPES 0xff + +/* DESC_CNT_TRSHLD */ +#define BAM_DESC_CNT_TRSHLD 0xffff + +/* IRQ_SRCS */ +#define BAM_IRQ 0x80000000 +#define P_IRQ 0x7fffffff + +#define IRQ_STTS_BAM_EMPTY_IRQ 0x8 +#define IRQ_STTS_BAM_ERROR_IRQ 0x4 +#define IRQ_STTS_BAM_HRESP_ERR_IRQ 0x2 +#define IRQ_CLR_BAM_EMPTY_CLR 0x8 +#define IRQ_CLR_BAM_ERROR_CLR 0x4 +#define IRQ_CLR_BAM_HRESP_ERR_CLR 0x2 +#define IRQ_EN_BAM_EMPTY_EN 0x8 +#define IRQ_EN_BAM_ERROR_EN 0x4 +#define IRQ_EN_BAM_HRESP_ERR_EN 0x2 +#define IRQ_SIC_SEL_BAM_IRQ_SIC_SEL 0x80000000 +#define IRQ_SIC_SEL_P_IRQ_SIC_SEL 0x7fffffff +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HVMID 0x7c0000 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_DIRECT_MODE 0x20000 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HCID 0x1f000 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HPROT 0xf00 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HBURST 0xe0 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HSIZE 0x18 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HWRITE 0x4 +#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HTRANS 0x3 +#define CNFG_BITS_BAM_AU_ACCUMED 0x4000000 +#define CNFG_BITS_BAM_PSM_P_HD_DATA 0x2000000 +#define CNFG_BITS_BAM_REG_P_EN 0x1000000 +#define CNFG_BITS_BAM_WB_DSC_AVL_P_RST 0x800000 +#define CNFG_BITS_BAM_WB_RETR_SVPNT 0x400000 +#define CNFG_BITS_BAM_WB_CSW_ACK_IDL 0x200000 +#define CNFG_BITS_BAM_WB_BLK_CSW 0x100000 +#define CNFG_BITS_BAM_WB_P_RES 0x80000 +#define CNFG_BITS_BAM_SI_P_RES 0x40000 +#define CNFG_BITS_BAM_AU_P_RES 0x20000 +#define CNFG_BITS_BAM_PSM_P_RES 0x10000 +#define CNFG_BITS_BAM_PSM_CSW_REQ 0x8000 +#define CNFG_BITS_BAM_SB_CLK_REQ 0x4000 +#define CNFG_BITS_BAM_IBC_DISABLE 0x2000 +#define CNFG_BITS_BAM_NO_EXT_P_RST 0x1000 +#define CNFG_BITS_BAM_FULL_PIPE 0x800 +#define CNFG_BITS_BAM_PIPE_CNFG 0x4 + +/* TEST_BUS_SEL */ +#define BAM_DATA_ERASE 0x40000 +#define BAM_DATA_FLUSH 0x20000 +#define BAM_CLK_ALWAYS_ON 0x10000 +#define BAM_TESTBUS_SEL 0x7f + +/* TRUST_REG */ +#define BAM_VMID 0x1f00 +#define BAM_RST_BLOCK 0x80 +#define BAM_EE 0x3 + +/* P_TRUST_REGn */ +#define BAM_P_VMID 0x1f00 +#define BAM_P_EE 0x3 + +/* P_PRDCR_SDBNDn */ +#define P_PRDCR_SDBNDn_BAM_P_SB_UPDATED 0x1000000 +#define P_PRDCR_SDBNDn_BAM_P_TOGGLE 0x100000 +#define P_PRDCR_SDBNDn_BAM_P_CTRL 0xf0000 +#define P_PRDCR_SDBNDn_BAM_P_BYTES_FREE 0xffff +/* P_CNSMR_SDBNDn */ +#define P_CNSMR_SDBNDn_BAM_P_SB_UPDATED 0x1000000 +#define P_CNSMR_SDBNDn_BAM_P_WAIT_4_ACK 0x800000 +#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE 0x400000 +#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE_R 0x200000 +#define P_CNSMR_SDBNDn_BAM_P_TOGGLE 0x100000 +#define P_CNSMR_SDBNDn_BAM_P_CTRL 0xf0000 +#define P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL 0xffff + +/* P_ctrln */ +#define P_PREFETCH_LIMIT 0x600 +#define P_AUTO_EOB_SEL 0x180 +#define P_AUTO_EOB 0x40 +#define P_SYS_MODE 0x20 +#define P_SYS_STRM 0x10 +#define P_DIRECTION 0x8 +#define P_EN 0x2 + +#define P_RST_P_SW_RST 0x1 + +#define P_HALT_P_PROD_HALTED 0x2 +#define P_HALT_P_HALT 0x1 + +#define P_IRQ_STTS_P_TRNSFR_END_IRQ 0x20 +#define P_IRQ_STTS_P_ERR_IRQ 0x10 +#define P_IRQ_STTS_P_OUT_OF_DESC_IRQ 0x8 +#define P_IRQ_STTS_P_WAKE_IRQ 0x4 +#define P_IRQ_STTS_P_TIMER_IRQ 0x2 +#define P_IRQ_STTS_P_PRCSD_DESC_IRQ 0x1 + +#define P_IRQ_CLR_P_TRNSFR_END_CLR 0x20 +#define P_IRQ_CLR_P_ERR_CLR 0x10 +#define P_IRQ_CLR_P_OUT_OF_DESC_CLR 0x8 +#define P_IRQ_CLR_P_WAKE_CLR 0x4 +#define P_IRQ_CLR_P_TIMER_CLR 0x2 +#define P_IRQ_CLR_P_PRCSD_DESC_CLR 0x1 + +#define P_IRQ_EN_P_TRNSFR_END_EN 0x20 +#define P_IRQ_EN_P_ERR_EN 0x10 +#define P_IRQ_EN_P_OUT_OF_DESC_EN 0x8 +#define P_IRQ_EN_P_WAKE_EN 0x4 +#define P_IRQ_EN_P_TIMER_EN 0x2 +#define P_IRQ_EN_P_PRCSD_DESC_EN 0x1 + +#define P_TIMER_P_TIMER 0xffff + +/* P_TIMER_ctrln */ +#define P_TIMER_RST 0x80000000 +#define P_TIMER_RUN 0x40000000 +#define P_TIMER_MODE 0x20000000 +#define P_TIMER_TRSHLD 0xffff + +/* P_EVNT_regn */ +#define P_BYTES_CONSUMED 0xffff0000 +#define P_DESC_FIFO_PEER_OFST 0xffff + +/* P_SW_ofstsn */ +#define SW_OFST_IN_DESC 0xffff0000 +#define SW_DESC_OFST 0xffff + +#define P_EVNT_GEN_TRSHLD_P_TRSHLD 0xffff + +/* P_FIFO_sizesn */ +#define P_DATA_FIFO_SIZE 0xffff0000 +#define P_DESC_FIFO_SIZE 0xffff + +#define P_RETR_CNTXT_RETR_DESC_OFST 0xffff0000 +#define P_RETR_CNTXT_RETR_OFST_IN_DESC 0xffff +#define P_SI_CNTXT_SI_DESC_OFST 0xffff +#define P_AU_PSM_CNTXT_1_AU_PSM_ACCUMED 0xffff0000 +#define P_AU_PSM_CNTXT_1_AU_ACKED 0xffff +#define P_PSM_CNTXT_2_PSM_DESC_VALID 0x80000000 +#define P_PSM_CNTXT_2_PSM_DESC_IRQ 0x40000000 +#define P_PSM_CNTXT_2_PSM_DESC_IRQ_DONE 0x20000000 +#define P_PSM_CNTXT_2_PSM_GENERAL_BITS 0x1e000000 +#define P_PSM_CNTXT_2_PSM_CONS_STATE 0x1c00000 +#define P_PSM_CNTXT_2_PSM_PROD_SYS_STATE 0x380000 +#define P_PSM_CNTXT_2_PSM_PROD_B2B_STATE 0x70000 +#define P_PSM_CNTXT_2_PSM_DESC_SIZE 0xffff +#define P_PSM_CNTXT_4_PSM_DESC_OFST 0xffff0000 +#define P_PSM_CNTXT_4_PSM_SAVED_ACCUMED_SIZE 0xffff +#define P_PSM_CNTXT_5_PSM_BLOCK_BYTE_CNT 0xffff0000 +#define P_PSM_CNTXT_5_PSM_OFST_IN_DESC 0xffff +#endif + +#define BAM_ERROR (-1) +#define BAM_INVALID_OFFSET 0xFFFFFFFF + +enum bam_regs { + CTRL, + REVISION, + SW_REVISION, + NUM_PIPES, + TIMER, + TIMER_CTRL, + DESC_CNT_TRSHLD, + IRQ_SRCS, + IRQ_SRCS_MSK, + IRQ_SRCS_UNMASKED, + IRQ_STTS, + IRQ_CLR, + IRQ_EN, + IRQ_SIC_SEL, + AHB_MASTER_ERR_CTRLS, + AHB_MASTER_ERR_ADDR, + AHB_MASTER_ERR_ADDR_MSB, + AHB_MASTER_ERR_DATA, + IRQ_DEST, + PERIPH_IRQ_DEST, + TRUST_REG, + TEST_BUS_SEL, + TEST_BUS_REG, + CNFG_BITS, + IRQ_SRCS_EE, + IRQ_SRCS_MSK_EE, + IRQ_SRCS_UNMASKED_EE, + PIPE_ATTR_EE, + P_CTRL, + P_RST, + P_HALT, + P_IRQ_STTS, + P_IRQ_CLR, + P_IRQ_EN, + P_TIMER, + P_TIMER_CTRL, + P_PRDCR_SDBND, + P_CNSMR_SDBND, + P_EVNT_DEST_ADDR, + P_EVNT_DEST_ADDR_MSB, + P_EVNT_REG, + P_SW_OFSTS, + P_DATA_FIFO_ADDR, + P_DATA_FIFO_ADDR_MSB, + P_DESC_FIFO_ADDR, + P_DESC_FIFO_ADDR_MSB, + P_EVNT_GEN_TRSHLD, + P_FIFO_SIZES, + P_IRQ_DEST_ADDR, + P_RETR_CNTXT, + P_SI_CNTXT, + P_DF_CNTXT, + P_AU_PSM_CNTXT_1, + P_PSM_CNTXT_2, + P_PSM_CNTXT_3, + P_PSM_CNTXT_3_MSB, + P_PSM_CNTXT_4, + P_PSM_CNTXT_5, + P_TRUST_REG, + BAM_MAX_REGS, +}; + +static u32 bam_regmap[][BAM_MAX_REGS] = { + { /* LEGACY BAM*/ + [CTRL] = 0xf80, + [REVISION] = 0xf84, + [NUM_PIPES] = 0xfbc, + [DESC_CNT_TRSHLD] = 0xf88, + [IRQ_SRCS] = 0xf8c, + [IRQ_SRCS_MSK] = 0xf90, + [IRQ_SRCS_UNMASKED] = 0xfb0, + [IRQ_STTS] = 0xf94, + [IRQ_CLR] = 0xf98, + [IRQ_EN] = 0xf9c, + [IRQ_SIC_SEL] = 0xfa0, + [AHB_MASTER_ERR_CTRLS] = 0xfa4, + [AHB_MASTER_ERR_ADDR] = 0xfa8, + [AHB_MASTER_ERR_DATA] = 0xfac, + [IRQ_DEST] = 0xfb4, + [PERIPH_IRQ_DEST] = 0xfb8, + [TRUST_REG] = 0xff0, + [TEST_BUS_SEL] = 0xff4, + [TEST_BUS_REG] = 0xff8, + [CNFG_BITS] = 0xffc, + [IRQ_SRCS_EE] = 0x1800, + [IRQ_SRCS_MSK_EE] = 0x1804, + [IRQ_SRCS_UNMASKED_EE] = 0x1808, + [P_CTRL] = 0x0, + [P_RST] = 0x4, + [P_HALT] = 0x8, + [P_IRQ_STTS] = 0x10, + [P_IRQ_CLR] = 0x14, + [P_IRQ_EN] = 0x18, + [P_TIMER] = 0x1c, + [P_TIMER_CTRL] = 0x20, + [P_PRDCR_SDBND] = 0x24, + [P_CNSMR_SDBND] = 0x28, + [P_EVNT_DEST_ADDR] = 0x102c, + [P_EVNT_REG] = 0x1018, + [P_SW_OFSTS] = 0x1000, + [P_DATA_FIFO_ADDR] = 0x1024, + [P_DESC_FIFO_ADDR] = 0x101c, + [P_EVNT_GEN_TRSHLD] = 0x1028, + [P_FIFO_SIZES] = 0x1020, + [P_IRQ_DEST_ADDR] = 0x103c, + [P_RETR_CNTXT] = 0x1034, + [P_SI_CNTXT] = 0x1038, + [P_AU_PSM_CNTXT_1] = 0x1004, + [P_PSM_CNTXT_2] = 0x1008, + [P_PSM_CNTXT_3] = 0x100c, + [P_PSM_CNTXT_4] = 0x1010, + [P_PSM_CNTXT_5] = 0x1014, + [P_TRUST_REG] = 0x30, + }, + { /* NDP BAM */ + [CTRL] = 0x0, + [REVISION] = 0x4, + [SW_REVISION] = 0x80, + [NUM_PIPES] = 0x3c, + [TIMER] = 0x40, + [TIMER_CTRL] = 0x44, + [DESC_CNT_TRSHLD] = 0x8, + [IRQ_SRCS] = 0xc, + [IRQ_SRCS_MSK] = 0x10, + [IRQ_SRCS_UNMASKED] = 0x30, + [IRQ_STTS] = 0x14, + [IRQ_CLR] = 0x18, + [IRQ_EN] = 0x1c, + [AHB_MASTER_ERR_CTRLS] = 0x24, + [AHB_MASTER_ERR_ADDR] = 0x28, + [AHB_MASTER_ERR_ADDR_MSB] = 0x104, + [AHB_MASTER_ERR_DATA] = 0x2c, + [TRUST_REG] = 0x70, + [TEST_BUS_SEL] = 0x74, + [TEST_BUS_REG] = 0x78, + [CNFG_BITS] = 0x7c, + [IRQ_SRCS_EE] = 0x800, + [IRQ_SRCS_MSK_EE] = 0x804, + [IRQ_SRCS_UNMASKED_EE] = 0x808, + [PIPE_ATTR_EE] = 0x80c, + [P_CTRL] = 0x1000, + [P_RST] = 0x1004, + [P_HALT] = 0x1008, + [P_IRQ_STTS] = 0x1010, + [P_IRQ_CLR] = 0x1014, + [P_IRQ_EN] = 0x1018, + [P_TIMER] = 0x101c, + [P_TIMER_CTRL] = 0x1020, + [P_PRDCR_SDBND] = 0x1024, + [P_CNSMR_SDBND] = 0x1028, + [P_EVNT_DEST_ADDR] = 0x182c, + [P_EVNT_DEST_ADDR_MSB] = 0x1934, + [P_EVNT_REG] = 0x1818, + [P_SW_OFSTS] = 0x1800, + [P_DATA_FIFO_ADDR] = 0x1824, + [P_DATA_FIFO_ADDR_MSB] = 0x1924, + [P_DESC_FIFO_ADDR] = 0x181c, + [P_DESC_FIFO_ADDR_MSB] = 0x1914, + [P_EVNT_GEN_TRSHLD] = 0x1828, + [P_FIFO_SIZES] = 0x1820, + [P_RETR_CNTXT] = 0x1834, + [P_SI_CNTXT] = 0x1838, + [P_DF_CNTXT] = 0x1830, + [P_AU_PSM_CNTXT_1] = 0x1804, + [P_PSM_CNTXT_2] = 0x1808, + [P_PSM_CNTXT_3] = 0x180c, + [P_PSM_CNTXT_3_MSB] = 0x1904, + [P_PSM_CNTXT_4] = 0x1810, + [P_PSM_CNTXT_5] = 0x1814, + [P_TRUST_REG] = 0x1030, + }, + { /* 4K OFFSETs*/ + [CTRL] = 0x0, + [REVISION] = 0x1000, + [SW_REVISION] = 0x1004, + [NUM_PIPES] = 0x1008, + [TIMER] = 0x40, + [TIMER_CTRL] = 0x44, + [DESC_CNT_TRSHLD] = 0x8, + [IRQ_SRCS] = 0x3010, + [IRQ_SRCS_MSK] = 0x3014, + [IRQ_SRCS_UNMASKED] = 0x3018, + [IRQ_STTS] = 0x14, + [IRQ_CLR] = 0x18, + [IRQ_EN] = 0x1c, + [AHB_MASTER_ERR_CTRLS] = 0x1024, + [AHB_MASTER_ERR_ADDR] = 0x1028, + [AHB_MASTER_ERR_ADDR_MSB] = 0x1104, + [AHB_MASTER_ERR_DATA] = 0x102c, + [TRUST_REG] = 0x2000, + [TEST_BUS_SEL] = 0x1010, + [TEST_BUS_REG] = 0x1014, + [CNFG_BITS] = 0x7c, + [IRQ_SRCS_EE] = 0x3000, + [IRQ_SRCS_MSK_EE] = 0x3004, + [IRQ_SRCS_UNMASKED_EE] = 0x3008, + [PIPE_ATTR_EE] = 0x300c, + [P_CTRL] = 0x13000, + [P_RST] = 0x13004, + [P_HALT] = 0x13008, + [P_IRQ_STTS] = 0x13010, + [P_IRQ_CLR] = 0x13014, + [P_IRQ_EN] = 0x13018, + [P_TIMER] = 0x1301c, + [P_TIMER_CTRL] = 0x13020, + [P_PRDCR_SDBND] = 0x13024, + [P_CNSMR_SDBND] = 0x13028, + [P_EVNT_DEST_ADDR] = 0x1382c, + [P_EVNT_DEST_ADDR_MSB] = 0x13934, + [P_EVNT_REG] = 0x13818, + [P_SW_OFSTS] = 0x13800, + [P_DATA_FIFO_ADDR] = 0x13824, + [P_DATA_FIFO_ADDR_MSB] = 0x13924, + [P_DESC_FIFO_ADDR] = 0x1381c, + [P_DESC_FIFO_ADDR_MSB] = 0x13914, + [P_EVNT_GEN_TRSHLD] = 0x13828, + [P_FIFO_SIZES] = 0x13820, + [P_RETR_CNTXT] = 0x13834, + [P_SI_CNTXT] = 0x13838, + [P_DF_CNTXT] = 0x13830, + [P_AU_PSM_CNTXT_1] = 0x13804, + [P_PSM_CNTXT_2] = 0x13808, + [P_PSM_CNTXT_3] = 0x1380c, + [P_PSM_CNTXT_3_MSB] = 0x13904, + [P_PSM_CNTXT_4] = 0x13810, + [P_PSM_CNTXT_5] = 0x13814, + [P_TRUST_REG] = 0x2020, + }, +}; + +/* AHB buffer error control */ +enum bam_nonsecure_reset { + BAM_NONSECURE_RESET_ENABLE = 0, + BAM_NONSECURE_RESET_DISABLE = 1, +}; + +static inline u32 bam_get_register_offset(void *base, enum bam_regs reg, + u32 param) +{ + int index = BAM_ERROR; + u32 offset = 0; + u32 *ptr_reg = bam_regmap[bam_type]; + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return SPS_ERROR; + } + + if (reg >= CTRL && reg < IRQ_SRCS_EE) + index = 0; + if (reg >= IRQ_SRCS_EE && reg < P_CTRL) + index = (bam_type == SPS_BAM_NDP_4K) ? 0x1000 : 0x80; + if (reg >= P_CTRL && reg < P_TRUST_REG) { + if (bam_type == SPS_BAM_LEGACY) { + if (reg >= P_EVNT_DEST_ADDR) + index = 0x40; + else + index = 0x80; + } else + index = 0x1000; + } else if (reg == P_TRUST_REG) { + if (bam_type == SPS_BAM_LEGACY) + index = 0x80; + else + index = (bam_type == SPS_BAM_NDP_4K) ? 0x4 : 0x1000; + } + if (index < 0) { + SPS_ERR(dev, "Failed to find register offset for %d\n", reg); + return BAM_INVALID_OFFSET; + } + + offset = *(ptr_reg + reg) + (index * param); + return offset; +} + + +/** + * + * Read register with debug info. + * + * @base - bam base virtual address. + * @offset - register offset. + * + * @return u32 + */ +static inline u32 bam_read_reg(void *base, enum bam_regs reg, u32 param) +{ + u32 val, offset = 0; + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return SPS_ERROR; + } + offset = bam_get_register_offset(base, reg, param); + if (offset == BAM_INVALID_OFFSET) { + SPS_ERR(dev, "Failed to get the register offset for %d\n", reg); + return offset; + } + val = ioread32(dev->base + offset); + SPS_DBG(dev, "sps:bam 0x%pK(va) offset 0x%x reg 0x%x r_val 0x%x\n", + dev->base, offset, reg, val); + return val; +} + +/** + * Read register masked field with debug info. + * + * @base - bam base virtual address. + * @offset - register offset. + * @mask - register bitmask. + * + * @return u32 + */ +static inline u32 bam_read_reg_field(void *base, enum bam_regs reg, u32 param, + const u32 mask) +{ + u32 val, shift, offset = 0; + struct sps_bam *dev = to_sps_bam_dev(base); + unsigned long lmask = mask; + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return SPS_ERROR; + } + shift = find_first_bit(&lmask, 32); + offset = bam_get_register_offset(base, reg, param); + if (offset == BAM_INVALID_OFFSET) { + SPS_ERR(dev, "Failed to get the register offset for %d\n", reg); + return offset; + } + val = ioread32(dev->base + offset); + val &= mask; /* clear other bits */ + val >>= shift; + SPS_DBG(dev, "sps:bam 0x%pK(va) read reg 0x%x mask 0x%x r_val 0x%x\n", + dev->base, offset, mask, val); + return val; +} + +/** + * + * Write register with debug info. + * + * @base - bam base virtual address. + * @offset - register offset. + * @val - value to write. + * + */ +static inline void bam_write_reg(void *base, enum bam_regs reg, + u32 param, u32 val) +{ + u32 offset = 0; + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + offset = bam_get_register_offset(base, reg, param); + if (offset == BAM_INVALID_OFFSET) { + SPS_ERR(dev, "Failed to get the register offset for %d\n", reg); + return; + } + iowrite32(val, dev->base + offset); + SPS_DBG(dev, "sps:bam 0x%pK(va) write reg 0x%x w_val 0x%x\n", + dev->base, offset, val); +} + +/** + * Write register masked field with debug info. + * + * @base - bam base virtual address. + * @offset - register offset. + * @mask - register bitmask. + * @val - value to write. + * + */ +static inline void bam_write_reg_field(void *base, enum bam_regs reg, + u32 param, const u32 mask, u32 val) +{ + u32 tmp, shift, offset = 0; + struct sps_bam *dev = to_sps_bam_dev(base); + unsigned long lmask = mask; + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + shift = find_first_bit(&lmask, 32); + offset = bam_get_register_offset(base, reg, param); + if (offset == BAM_INVALID_OFFSET) { + SPS_ERR(dev, "Failed to get the register offset for %d\n", reg); + return; + } + tmp = ioread32(dev->base + offset); + + tmp &= ~mask; /* clear written bits */ + val = tmp | (val << shift); + iowrite32(val, dev->base + offset); + SPS_DBG(dev, "sps:bam 0x%pK(va) write reg 0x%x w_val 0x%x\n", + dev->base, offset, val); +} + +/** + * Initialize a BAM device + * + */ +int bam_init(void *base, u32 ee, + u16 summing_threshold, + u32 irq_mask, u32 *version, + u32 *num_pipes, u32 options) +{ + u32 cfg_bits; + u32 ver = 0; + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return SPS_ERROR; + } + + SPS_DBG3(dev, "sps: bam=%pa 0x%pK(va).ee=%d\n", + BAM_ID(dev), dev->base, ee); + + ver = bam_read_reg_field(base, REVISION, 0, BAM_REVISION); + + if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) { + SPS_ERR(dev, "sps:bam 0x%pK(va) Invalid BAM REVISION 0x%x\n", + dev->base, ver); + return -ENODEV; + } + + SPS_DBG(dev, "sps:REVISION of BAM 0x%pK is 0x%x\n", + dev->base, ver); + + if (summing_threshold == 0) { + summing_threshold = 4; + SPS_ERR(dev, + "sps:bam 0x%pK(va) summing_threshold is zero,use default 4\n", + dev->base); + } + + if (options & SPS_BAM_NO_EXT_P_RST) + cfg_bits = 0xffffffff & ~(3 << 11); + else + cfg_bits = 0xffffffff & ~(1 << 11); + + bam_write_reg_field(base, CTRL, 0, BAM_SW_RST, 1); + /* No delay needed */ + bam_write_reg_field(base, CTRL, 0, BAM_SW_RST, 0); + + bam_write_reg_field(base, CTRL, 0, BAM_EN, 1); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + bam_write_reg_field(base, CTRL, 0, CACHE_MISS_ERR_RESP_EN, 0); + + if (options & SPS_BAM_NO_LOCAL_CLK_GATING) + bam_write_reg_field(base, CTRL, 0, LOCAL_CLK_GATING, 0); + else + bam_write_reg_field(base, CTRL, 0, LOCAL_CLK_GATING, 1); + + if (enhd_pipe) { + if (options & SPS_BAM_CANCEL_WB) + bam_write_reg_field(base, CTRL, 0, + BAM_MESS_ONLY_CANCEL_WB, 1); + else + bam_write_reg_field(base, CTRL, 0, + BAM_MESS_ONLY_CANCEL_WB, 0); + } +#endif + bam_write_reg(base, DESC_CNT_TRSHLD, 0, summing_threshold); + + bam_write_reg(base, CNFG_BITS, 0, cfg_bits); + + /* + * Enable Global BAM Interrupt - for error reasons , + * filter with mask. + * Note: Pipes interrupts are disabled until BAM_P_IRQ_enn is set + */ + bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, BAM_IRQ, 1); + + bam_write_reg(base, IRQ_EN, 0, irq_mask); + + *num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES); + + *version = ver; + + return 0; +} + +/** + * Set BAM global execution environment + * + * @base - BAM virtual base address + * + * @ee - BAM execution environment index + * + * @vmid - virtual master identifier + * + * @reset - enable/disable BAM global software reset + */ +static void bam_set_ee(void *base, u32 ee, u32 vmid, + enum bam_nonsecure_reset reset) +{ + bam_write_reg_field(base, TRUST_REG, 0, BAM_EE, ee); + bam_write_reg_field(base, TRUST_REG, 0, BAM_VMID, vmid); + bam_write_reg_field(base, TRUST_REG, 0, BAM_RST_BLOCK, reset); +} + +/** + * Set the pipe execution environment + * + * @base - BAM virtual base address + * + * @pipe - pipe index + * + * @ee - BAM execution environment index + * + * @vmid - virtual master identifier + */ +static void bam_pipe_set_ee(void *base, u32 pipe, u32 ee, u32 vmid) +{ + bam_write_reg_field(base, P_TRUST_REG, pipe, BAM_P_EE, ee); + bam_write_reg_field(base, P_TRUST_REG, pipe, BAM_P_VMID, vmid); +} + +/** + * Initialize BAM device security execution environment + */ +int bam_security_init(void *base, u32 ee, u32 vmid, u32 pipe_mask) +{ + u32 version; + u32 num_pipes; + u32 mask; + u32 pipe; + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return SPS_ERROR; + } + + SPS_DBG3(dev, "sps: bam=%pa 0x%pK(va)\n", BAM_ID(dev), dev->base); + + /* + * Discover the hardware version number and the number of pipes + * supported by this BAM + */ + version = bam_read_reg_field(base, REVISION, 0, BAM_REVISION); + num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES); + if (version < 3 || version > 0x1F) { + SPS_ERR(dev, + "sps:bam 0x%pK(va) security is not supported for this BAM version 0x%x\n", + dev->base, version); + return -ENODEV; + } + + if (num_pipes > BAM_MAX_PIPES) { + SPS_ERR(dev, + "sps:bam 0x%pK(va) the number of pipes is more than the maximum number allowed\n", + dev->base); + return -ENODEV; + } + + for (pipe = 0, mask = 1; pipe < num_pipes; pipe++, mask <<= 1) + if ((mask & pipe_mask) != 0) + bam_pipe_set_ee(base, pipe, ee, vmid); + + /* If MSbit is set, assign top-level interrupt to this EE */ + mask = 1UL << 31; + if ((mask & pipe_mask) != 0) + bam_set_ee(base, ee, vmid, BAM_NONSECURE_RESET_ENABLE); + + return 0; +} + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM +static inline u32 bam_get_pipe_attr(void *base, u32 ee, bool global) +{ + u32 val; + + if (global) + val = bam_read_reg_field(base, PIPE_ATTR_EE, ee, BAM_ENABLED); + else + val = bam_read_reg_field(base, PIPE_ATTR_EE, ee, P_ATTR); + + return val; +} +#else +static inline u32 bam_get_pipe_attr(void *base, u32 ee, bool global) +{ + return 0; +} +#endif + +/** + * Verify that a BAM device is enabled and gathers the hardware + * configuration. + * + */ +int bam_check(void *base, u32 *version, u32 ee, u32 *num_pipes) +{ + u32 ver = 0; + u32 enabled = 0; + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return SPS_ERROR; + } + + SPS_DBG3(dev, "sps: bam=%pa 0x%pK(va)\n", BAM_ID(dev), dev->base); + + if (!enhd_pipe) + enabled = bam_read_reg_field(base, CTRL, 0, BAM_EN); + else + enabled = bam_get_pipe_attr(base, ee, true); + + if (!enabled) { + SPS_ERR(dev, "sps: bam 0x%pK(va) is not enabled\n", dev->base); + return -ENODEV; + } + + ver = bam_read_reg(base, REVISION, 0) & BAM_REVISION; + + /* + * Discover the hardware version number and the number of pipes + * supported by this BAM + */ + *num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES); + *version = ver; + + /* Check BAM version */ + if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) { + SPS_ERR(dev, "sps: bam 0x%pK(va) Invalid BAM version 0x%x\n", + dev->base, ver); + return -ENODEV; + } + + return 0; +} + +/** + * Disable a BAM device + * + */ +void bam_exit(void *base, u32 ee) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + SPS_DBG3(dev, "sps: bam=%pa 0x%pK(va).ee=%d\n", BAM_ID(dev), + dev->base, ee); + + bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, BAM_IRQ, 0); + + bam_write_reg(base, IRQ_EN, 0, 0); + + /* Disable the BAM */ + bam_write_reg_field(base, CTRL, 0, BAM_EN, 0); +} + +/** + * Output BAM register content + * including the TEST_BUS register content under + * different TEST_BUS_SEL values. + */ +void bam_output_register_content(void *base, u32 ee) +{ + u32 num_pipes; + u32 i; + u32 pipe_attr = 0; + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + + print_bam_test_bus_reg(base, 0); + + print_bam_selected_reg(base, BAM_MAX_EES); + + num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, + BAM_NUM_PIPES); + SPS_INFO(dev, "sps:bam %pa 0x%pK(va) has %d pipes\n", + BAM_ID(dev), dev->base, num_pipes); + + pipe_attr = enhd_pipe ? + bam_get_pipe_attr(base, ee, false) : 0x0; + + if (!enhd_pipe || !pipe_attr) + for (i = 0; i < num_pipes; i++) + print_bam_pipe_selected_reg(base, i); + else { + for (i = 0; i < num_pipes; i++) { + if (pipe_attr & (1UL << i)) + print_bam_pipe_selected_reg(base, i); + } + } +} + +/** + * Get BAM IRQ source and clear global IRQ status + */ +u32 bam_check_irq_source(void *base, u32 ee, u32 mask, + enum sps_callback_case *cb_case) +{ + u32 source = 0, clr = 0; + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return SPS_ERROR; + } + source = bam_read_reg(base, IRQ_SRCS_EE, ee); + clr = source & (1UL << 31); + + if (clr) { + u32 status = 0; + + status = bam_read_reg(base, IRQ_STTS, 0); + + if (status & IRQ_STTS_BAM_ERROR_IRQ) { + SPS_ERR(dev, + "sps:bam %pa 0x%pK(va);bam irq status=0x%x\nsps: BAM_ERROR_IRQ\n", + BAM_ID(dev), dev->base, status); + bam_output_register_content(base, ee); + *cb_case = SPS_CALLBACK_BAM_ERROR_IRQ; + } else if (status & IRQ_STTS_BAM_HRESP_ERR_IRQ) { + SPS_ERR(dev, + "sps:bam %pa 0x%pK(va);bam irq status=0x%x\nsps: BAM_HRESP_ERR_IRQ\n", + BAM_ID(dev), dev->base, status); + bam_output_register_content(base, ee); + *cb_case = SPS_CALLBACK_BAM_HRESP_ERR_IRQ; +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + } else if (status & IRQ_STTS_BAM_TIMER_IRQ) { + SPS_DBG1(dev, + "sps:bam 0x%pK(va);receive BAM_TIMER_IRQ\n", + dev->base); + *cb_case = SPS_CALLBACK_BAM_TIMER_IRQ; +#endif + } else + SPS_INFO(dev, + "sps:bam %pa 0x%pK(va);bam irq status=0x%x\n", + BAM_ID(dev), dev->base, status); + + bam_write_reg(base, IRQ_CLR, 0, status); + } + + source &= (mask|(1UL << 31)); + return source; +} + +/* + * Reset a BAM pipe + */ +void bam_pipe_reset(void *base, u32 pipe) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + SPS_DBG2(dev, "sps: bam=%pa 0x%pK(va).pipe=%d\n", BAM_ID(dev), + dev->base, pipe); + + bam_write_reg(base, P_RST, pipe, 1); + wmb(); /* ensure pipe is reset */ + bam_write_reg(base, P_RST, pipe, 0); + wmb(); /* ensure pipe reset is de-asserted*/ +} + +/* + * Disable a BAM pipe + */ +void bam_disable_pipe(void *base, u32 pipe) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + SPS_DBG2(dev, "sps: bam=0x%pK(va).pipe=%d\n", base, pipe); + bam_write_reg_field(base, P_CTRL, pipe, P_EN, 0); + wmb(); /* ensure pipe is disabled */ +} + +/* + * Check if the last desc is ZLT + */ +bool bam_pipe_check_zlt(void *base, u32 pipe) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return false; + } + + if (bam_read_reg_field(base, P_HALT, pipe, P_HALT_P_LAST_DESC_ZLT)) { + SPS_DBG(dev, + "sps: bam=0x%pK(va).pipe=%d: the last desc is ZLT\n", + base, pipe); + return true; + } + + SPS_DBG(dev, + "sps: bam=0x%pK(va).pipe=%d: the last desc is not ZLT\n", + base, pipe); + return false; +} + +/* + * Check if desc FIFO is empty + */ +bool bam_pipe_check_pipe_empty(void *base, u32 pipe) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return false; + } + + if (bam_read_reg_field(base, P_HALT, pipe, P_HALT_P_PIPE_EMPTY)) { + SPS_DBG(dev, + "sps: bam=0x%pK(va).pipe=%d: desc FIFO is empty\n", + base, pipe); + return true; + } + + SPS_DBG(dev, + "sps: bam=0x%pK(va).pipe=%d: desc FIFO is not empty\n", + base, pipe); + return false; +} + +/** + * Initialize a BAM pipe + */ +int bam_pipe_init(void *base, u32 pipe, struct bam_pipe_parameters *param, + u32 ee) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return SPS_ERROR; + } + SPS_DBG2(dev, "sps: bam=%pa 0x%pK(va).pipe=%d\n", + BAM_ID(dev), dev->base, pipe); + + /* Reset the BAM pipe */ + bam_write_reg(base, P_RST, pipe, 1); + /* No delay needed */ + bam_write_reg(base, P_RST, pipe, 0); + + /* Enable the Pipe Interrupt at the BAM level */ + bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, (1 << pipe), 1); + + bam_write_reg(base, P_IRQ_EN, pipe, param->pipe_irq_mask); + + bam_write_reg_field(base, P_CTRL, pipe, P_DIRECTION, param->dir); + bam_write_reg_field(base, P_CTRL, pipe, P_SYS_MODE, param->mode); + + bam_write_reg(base, P_EVNT_GEN_TRSHLD, pipe, param->event_threshold); + + bam_write_reg(base, P_DESC_FIFO_ADDR, pipe, + SPS_GET_LOWER_ADDR(param->desc_base)); + bam_write_reg_field(base, P_FIFO_SIZES, pipe, P_DESC_FIFO_SIZE, + param->desc_size); + + bam_write_reg_field(base, P_CTRL, pipe, P_SYS_STRM, + param->stream_mode); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + if (SPS_LPAE && SPS_GET_UPPER_ADDR(param->desc_base)) + bam_write_reg(base, P_DESC_FIFO_ADDR_MSB, pipe, + SPS_GET_UPPER_ADDR(param->desc_base)); + + bam_write_reg_field(base, P_CTRL, pipe, P_LOCK_GROUP, + param->lock_group); + + SPS_DBG(dev, "sps:bam=0x%pK(va).pipe=%d.lock_group=%d\n", + dev->base, pipe, param->lock_group); +#endif + + if (param->mode == BAM_PIPE_MODE_BAM2BAM) { + u32 peer_dest_addr = param->peer_phys_addr + + bam_get_register_offset(base, P_EVNT_REG, + param->peer_pipe); + + bam_write_reg(base, P_DATA_FIFO_ADDR, pipe, + SPS_GET_LOWER_ADDR(param->data_base)); + bam_write_reg_field(base, P_FIFO_SIZES, pipe, + P_DATA_FIFO_SIZE, param->data_size); + + if (!(param->dummy_peer)) { + bam_write_reg(base, P_EVNT_DEST_ADDR, pipe, + peer_dest_addr); + } else { + bam_write_reg(base, P_EVNT_DEST_ADDR, pipe, + param->peer_phys_addr); + } + SPS_DBG2(dev, + "sps:bam=0x%pK(va).pipe=%d.peer_bam=0x%x.peer_pipe=%d\n", + dev->base, pipe, + (u32) param->peer_phys_addr, + param->peer_pipe); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + if (SPS_LPAE && SPS_GET_UPPER_ADDR(param->data_base)) { + bam_write_reg(base, P_EVNT_DEST_ADDR_MSB, pipe, 0x0); + bam_write_reg(base, P_DATA_FIFO_ADDR_MSB, pipe, + SPS_GET_UPPER_ADDR(param->data_base)); + } + + bam_write_reg_field(base, P_CTRL, pipe, P_WRITE_NWD, + param->write_nwd); + + SPS_DBG(dev, "sps:%s WRITE_NWD bit for this bam2bam pipe\n", + param->write_nwd ? "Set" : "Do not set"); +#endif + } + + /* Pipe Enable - at last */ + bam_write_reg_field(base, P_CTRL, pipe, P_EN, 1); + + return 0; +} + +/** + * Reset the BAM pipe + * + */ +void bam_pipe_exit(void *base, u32 pipe, u32 ee) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + SPS_DBG2(dev, "sps: bam=%pa 0x%pK(va).pipe=%d\n", + BAM_ID(dev), dev->base, pipe); + + bam_write_reg(base, P_IRQ_EN, pipe, 0); + + /* Disable the Pipe Interrupt at the BAM level */ + bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, (1 << pipe), 0); + + /* Pipe Disable */ + bam_write_reg_field(base, P_CTRL, pipe, P_EN, 0); +} + +/** + * Enable a BAM pipe + * + */ +void bam_pipe_enable(void *base, u32 pipe) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + SPS_DBG2(dev, "sps: bam=%pa 0x%pK(va).pipe=%d\n", + BAM_ID(dev), dev->base, pipe); + + if (bam_read_reg_field(base, P_CTRL, pipe, P_EN)) + SPS_DBG2(dev, "sps:bam=0x%pK(va).pipe=%d is already enabled\n", + dev->base, pipe); + else + bam_write_reg_field(base, P_CTRL, pipe, P_EN, 1); +} + +/** + * Diasble a BAM pipe + * + */ +void bam_pipe_disable(void *base, u32 pipe) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + SPS_DBG2(dev, "sps: bam=%pa 0x%pK(va).pipe=%d\n", + BAM_ID(dev), dev->base, pipe); + + bam_write_reg_field(base, P_CTRL, pipe, P_EN, 0); +} + +/** + * Check if a BAM pipe is enabled. + * + */ +int bam_pipe_is_enabled(void *base, u32 pipe) +{ + return bam_read_reg_field(base, P_CTRL, pipe, P_EN); +} + +/** + * Configure interrupt for a BAM pipe + * + */ +void bam_pipe_set_irq(void *base, u32 pipe, enum bam_enable irq_en, + u32 src_mask, u32 ee) +{ + struct sps_bam *dev = to_sps_bam_dev(base); + + if ((dev == NULL) || (&dev->base != base)) { + SPS_ERR(sps, "Failed to get dev for base addr 0x%pK\n", base); + return; + } + SPS_DBG2(dev, + "sps: bam=%pa 0x%pK(va).pipe=%d; irq_en:%d; src_mask:0x%x; ee:%d\n", + BAM_ID(dev), dev->base, pipe, + irq_en, src_mask, ee); + if (src_mask & BAM_PIPE_IRQ_RST_ERROR) { + if (enhd_pipe) + bam_write_reg_field(base, IRQ_EN, 0, + IRQ_EN_BAM_ERROR_EN, 0); + else { + src_mask &= ~BAM_PIPE_IRQ_RST_ERROR; + SPS_DBG2(dev, + "SPS_O_RST_ERROR is not supported, pipe %d\n", + pipe); + } + } + if (src_mask & BAM_PIPE_IRQ_HRESP_ERROR) { + if (enhd_pipe) + bam_write_reg_field(base, IRQ_EN, 0, + IRQ_EN_BAM_HRESP_ERR_EN, 0); + else { + src_mask &= ~BAM_PIPE_IRQ_HRESP_ERROR; + SPS_DBG2(dev, + "SPS_O_HRESP_ERROR is not supported, pipe %d\n", + pipe); + } + } + + bam_write_reg(base, P_IRQ_EN, pipe, src_mask); + bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, (1 << pipe), irq_en); +} + +/** + * Configure a BAM pipe for satellite MTI use + * + */ +void bam_pipe_satellite_mti(void *base, u32 pipe, u32 irq_gen_addr, u32 ee) +{ + bam_write_reg(base, P_IRQ_EN, pipe, 0); +#ifndef CONFIG_SPS_SUPPORT_NDP_BAM + bam_write_reg(base, P_IRQ_DEST_ADDR, pipe, irq_gen_addr); + bam_write_reg_field(base, IRQ_SIC_SEL, 0, (1 << pipe), 1); +#endif + bam_write_reg_field(base, IRQ_SRCS_MSK, 0, (1 << pipe), 1); +} + +/** + * Configure MTI for a BAM pipe + * + */ +void bam_pipe_set_mti(void *base, u32 pipe, enum bam_enable irq_en, + u32 src_mask, u32 irq_gen_addr) +{ + /* + * MTI use is only supported on BAMs when global config is controlled + * by a remote processor. + * Consequently, the global configuration register to enable SIC (MTI) + * support cannot be accessed. + * The remote processor must be relied upon to enable the SIC and the + * interrupt. Since the remote processor enable both SIC and interrupt, + * the interrupt enable mask must be set to zero for polling mode. + */ +#ifndef CONFIG_SPS_SUPPORT_NDP_BAM + bam_write_reg(base, P_IRQ_DEST_ADDR, pipe, irq_gen_addr); +#endif + if (!irq_en) + src_mask = 0; + + bam_write_reg(base, P_IRQ_EN, pipe, src_mask); +} + +/** + * Get and Clear BAM pipe IRQ status + * + */ +u32 bam_pipe_get_and_clear_irq_status(void *base, u32 pipe) +{ + u32 status = 0; + + status = bam_read_reg(base, P_IRQ_STTS, pipe); + bam_write_reg(base, P_IRQ_CLR, pipe, status); + + return status; +} + +/** + * Set write offset for a BAM pipe + * + */ +void bam_pipe_set_desc_write_offset(void *base, u32 pipe, u32 next_write) +{ + /* + * It is not necessary to perform a read-modify-write masking to write + * the P_DESC_FIFO_PEER_OFST value, since the other field in the + * register (P_BYTES_CONSUMED) is read-only. + */ + bam_write_reg_field(base, P_EVNT_REG, pipe, P_DESC_FIFO_PEER_OFST, + next_write); +} + +/** + * Get write offset for a BAM pipe + * + */ +u32 bam_pipe_get_desc_write_offset(void *base, u32 pipe) +{ + return bam_read_reg_field(base, P_EVNT_REG, pipe, + P_DESC_FIFO_PEER_OFST); +} + +/** + * Get read offset for a BAM pipe + * + */ +u32 bam_pipe_get_desc_read_offset(void *base, u32 pipe) +{ + return bam_read_reg_field(base, P_SW_OFSTS, pipe, SW_DESC_OFST); +} + +/** + * Configure inactivity timer count for a BAM pipe + * + */ +void bam_pipe_timer_config(void *base, u32 pipe, enum bam_pipe_timer_mode mode, + u32 timeout_count) +{ + u32 for_all_pipes = 0; + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + for_all_pipes = bam_read_reg_field(base, REVISION, 0, + BAM_NUM_INACTIV_TMRS); +#endif + + if (for_all_pipes) { +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + bam_write_reg_field(base, TIMER_CTRL, 0, TIMER_MODE, mode); + bam_write_reg_field(base, TIMER_CTRL, 0, TIMER_TRSHLD, + timeout_count); +#endif + } else { + bam_write_reg_field(base, P_TIMER_CTRL, pipe, P_TIMER_MODE, + mode); + bam_write_reg_field(base, P_TIMER_CTRL, pipe, P_TIMER_TRSHLD, + timeout_count); + } +} + +/** + * Reset inactivity timer for a BAM pipe + * + */ +void bam_pipe_timer_reset(void *base, u32 pipe) +{ + u32 for_all_pipes = 0; + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + for_all_pipes = bam_read_reg_field(base, REVISION, 0, + BAM_NUM_INACTIV_TMRS); +#endif + + if (for_all_pipes) { +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + /* reset */ + bam_write_reg_field(base, TIMER_CTRL, 0, TIMER_RST, 0); + /* active */ + bam_write_reg_field(base, TIMER_CTRL, 0, TIMER_RST, 1); +#endif + } else { + /* reset */ + bam_write_reg_field(base, P_TIMER_CTRL, pipe, P_TIMER_RST, 0); + /* active */ + bam_write_reg_field(base, P_TIMER_CTRL, pipe, P_TIMER_RST, 1); + } +} + +/** + * Get inactivity timer count for a BAM pipe + * + */ +u32 bam_pipe_timer_get_count(void *base, u32 pipe) +{ + return bam_read_reg(base, P_TIMER, pipe); +} + +/* halt and un-halt a pipe */ +void bam_pipe_halt(void *base, u32 pipe, bool halt) +{ + if (halt) + bam_write_reg_field(base, P_HALT, pipe, P_HALT_P_HALT, 1); + else + bam_write_reg_field(base, P_HALT, pipe, P_HALT_P_HALT, 0); +} + +/* output the content of BAM-level registers */ +void print_bam_reg(void *virt_addr) +{ + int i, n, index = 0; + u32 *bam = (u32 *) virt_addr; + u32 ctrl; + u32 ver; + u32 pipes; + u32 offset = 0; + + if (bam == NULL) + return; + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + if (bam_type == SPS_BAM_NDP_4K) { + ctrl = bam[0x0 / 4]; + ver = bam[0x1000 / 4]; + pipes = bam[0x1008 / 4]; + } else { + ctrl = bam[0x0 / 4]; + ver = bam[0x4 / 4]; + pipes = bam[0x3c / 4]; + } +#else + ctrl = bam[0xf80 / 4]; + ver = bam[0xf84 / 4]; + pipes = bam[0xfbc / 4]; +#endif + + SPS_DUMP("%s", + "\nsps: --- Content of BAM-level registers---\n"); + + SPS_DUMP("BAM_CTRL: 0x%x\n", ctrl); + SPS_DUMP("BAM_REVISION: 0x%x\n", ver); + SPS_DUMP("NUM_PIPES: 0x%x\n", pipes); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + if (bam_type == SPS_BAM_NDP_4K) + offset = 0x301c; + else + offset = 0x80; + for (i = 0x0; i < offset; i += 0x10) + +#else + for (i = 0xf80; i < 0x1000; i += 0x10) +#endif + SPS_DUMP("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x\n", i, + bam[i / 4], bam[(i / 4) + 1], + bam[(i / 4) + 2], bam[(i / 4) + 3]); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + if (bam_type == SPS_BAM_NDP_4K) { + offset = 0x3000; + index = 0x1000; + } else { + offset = 0x800; + index = 0x80; + } + for (i = offset, n = 0; n++ < 8; i += index) +#else + for (i = 0x1800, n = 0; n++ < 4; i += 0x80) +#endif + SPS_DUMP("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x\n", i, + bam[i / 4], bam[(i / 4) + 1], + bam[(i / 4) + 2], bam[(i / 4) + 3]); + + SPS_DUMP("%s", + "\nsps: --- Content of BAM-level registers ---\n"); +} + +/* output the content of BAM pipe registers */ +void print_bam_pipe_reg(void *virt_addr, u32 pipe_index) +{ + int i; + u32 *bam = (u32 *) virt_addr; + u32 pipe = pipe_index; + u32 offset = 0; + + if (bam == NULL) + return; + + SPS_DUMP("\nsps: --- Content of Pipe %d registers ---\n", + pipe); + + SPS_DUMP("%s", "-- Pipe Management Registers --\n"); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + if (bam_type == SPS_BAM_NDP_4K) + offset = 0x13000; + else + offset = 0x1000; + for (i = offset + 0x1000 * pipe; i < offset + 0x1000 * pipe + 0x80; + i += 0x10) +#else + for (i = 0x0000 + 0x80 * pipe; i < 0x0000 + 0x80 * (pipe + 1); + i += 0x10) +#endif + SPS_DUMP("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x\n", i, + bam[i / 4], bam[(i / 4) + 1], + bam[(i / 4) + 2], bam[(i / 4) + 3]); + + SPS_DUMP("%s", + "-- Pipe Configuration and Internal State Registers --\n"); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + if (bam_type == SPS_BAM_NDP_4K) + offset = 0x13800; + else + offset = 0x1800; + for (i = offset + 0x1000 * pipe; i < offset + 0x1000 * pipe + 0x40; + i += 0x10) +#else + for (i = 0x1000 + 0x40 * pipe; i < 0x1000 + 0x40 * (pipe + 1); + i += 0x10) +#endif + SPS_DUMP("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x\n", i, + bam[i / 4], bam[(i / 4) + 1], + bam[(i / 4) + 2], bam[(i / 4) + 3]); + + SPS_DUMP("\nsps: --- Content of Pipe %d registers ---\n", + pipe); +} + +/* output the content of selected BAM-level registers */ +void print_bam_selected_reg(void *virt_addr, u32 ee) +{ + void *base = virt_addr; + + u32 bam_ctrl; + u32 bam_revision; + u32 bam_rev_num; + u32 bam_rev_ee_num; + + u32 bam_num_pipes; + u32 bam_pipe_num; + u32 bam_data_addr_bus_width; + + u32 bam_desc_cnt_trshld; + u32 bam_desc_cnt_trd_val; + + u32 bam_irq_en; + u32 bam_irq_stts; + + u32 bam_irq_src_ee = 0; + u32 bam_irq_msk_ee = 0; + u32 bam_irq_unmsk_ee = 0; + u32 bam_pipe_attr_ee = 0; + + u32 bam_ahb_err_ctrl; + u32 bam_ahb_err_addr; + u32 bam_ahb_err_data; + u32 bam_cnfg_bits; + + u32 bam_sw_rev = 0; + u32 bam_timer = 0; + u32 bam_timer_ctrl = 0; + u32 bam_ahb_err_addr_msb = 0; + + if (base == NULL) + return; + + bam_ctrl = bam_read_reg(base, CTRL, 0); + bam_revision = bam_read_reg(base, REVISION, 0); + bam_rev_num = bam_read_reg_field(base, REVISION, 0, BAM_REVISION); + bam_rev_ee_num = bam_read_reg_field(base, REVISION, 0, BAM_NUM_EES); + + bam_num_pipes = bam_read_reg(base, NUM_PIPES, 0); + bam_pipe_num = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES); + bam_data_addr_bus_width = bam_read_reg_field(base, NUM_PIPES, 0, + BAM_DATA_ADDR_BUS_WIDTH); + + bam_desc_cnt_trshld = bam_read_reg(base, DESC_CNT_TRSHLD, 0); + bam_desc_cnt_trd_val = bam_read_reg_field(base, DESC_CNT_TRSHLD, 0, + BAM_DESC_CNT_TRSHLD); + + bam_irq_en = bam_read_reg(base, IRQ_EN, 0); + bam_irq_stts = bam_read_reg(base, IRQ_STTS, 0); + + if (ee < BAM_MAX_EES) { + bam_irq_src_ee = bam_read_reg(base, IRQ_SRCS_EE, ee); + bam_irq_msk_ee = bam_read_reg(base, IRQ_SRCS_MSK_EE, ee); + bam_irq_unmsk_ee = bam_read_reg(base, IRQ_SRCS_UNMASKED_EE, ee); + } + + bam_ahb_err_ctrl = bam_read_reg(base, AHB_MASTER_ERR_CTRLS, 0); + bam_ahb_err_addr = bam_read_reg(base, AHB_MASTER_ERR_ADDR, 0); + bam_ahb_err_data = bam_read_reg(base, AHB_MASTER_ERR_DATA, 0); + bam_cnfg_bits = bam_read_reg(base, CNFG_BITS, 0); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + bam_sw_rev = bam_read_reg(base, SW_REVISION, 0); + bam_timer = bam_read_reg(base, TIMER, 0); + bam_timer_ctrl = bam_read_reg(base, TIMER_CTRL, 0); + bam_ahb_err_addr_msb = SPS_LPAE ? + bam_read_reg(base, AHB_MASTER_ERR_ADDR_MSB, 0) : 0; + if (ee < BAM_MAX_EES) + bam_pipe_attr_ee = enhd_pipe ? + bam_read_reg(base, PIPE_ATTR_EE, ee) : 0x0; +#endif + + + SPS_DUMP("%s", "\nsps: --- BAM-level registers ---\n\n"); + + SPS_DUMP("BAM_CTRL: 0x%x\n", bam_ctrl); + SPS_DUMP("BAM_REVISION: 0x%x\n", bam_revision); + SPS_DUMP(" REVISION: 0x%x\n", bam_rev_num); + SPS_DUMP(" NUM_EES: %d\n", bam_rev_ee_num); + SPS_DUMP("BAM_SW_REVISION: 0x%x\n", bam_sw_rev); + SPS_DUMP("BAM_NUM_PIPES: %d\n", bam_num_pipes); + SPS_DUMP("BAM_DATA_ADDR_BUS_WIDTH: %d\n", + ((bam_data_addr_bus_width == 0x0) ? 32 : 36)); + SPS_DUMP(" NUM_PIPES: %d\n", bam_pipe_num); + SPS_DUMP("BAM_DESC_CNT_TRSHLD: 0x%x\n", bam_desc_cnt_trshld); + SPS_DUMP(" DESC_CNT_TRSHLD: 0x%x (%d)\n", bam_desc_cnt_trd_val, + bam_desc_cnt_trd_val); + + SPS_DUMP("BAM_IRQ_EN: 0x%x\n", bam_irq_en); + SPS_DUMP("BAM_IRQ_STTS: 0x%x\n", bam_irq_stts); + + if (ee < BAM_MAX_EES) { + SPS_DUMP("BAM_IRQ_SRCS_EE(%d): 0x%x\n", ee, bam_irq_src_ee); + SPS_DUMP("BAM_IRQ_SRCS_MSK_EE(%d): 0x%x\n", ee, bam_irq_msk_ee); + SPS_DUMP("BAM_IRQ_SRCS_UNMASKED_EE(%d): 0x%x\n", ee, + bam_irq_unmsk_ee); + SPS_DUMP("BAM_PIPE_ATTR_EE(%d): 0x%x\n", ee, bam_pipe_attr_ee); + } + + SPS_DUMP("BAM_AHB_MASTER_ERR_CTRLS: 0x%x\n", bam_ahb_err_ctrl); + SPS_DUMP("BAM_AHB_MASTER_ERR_ADDR: 0x%x\n", bam_ahb_err_addr); + SPS_DUMP("BAM_AHB_MASTER_ERR_ADDR_MSB: 0x%x\n", bam_ahb_err_addr_msb); + SPS_DUMP("BAM_AHB_MASTER_ERR_DATA: 0x%x\n", bam_ahb_err_data); + + SPS_DUMP("BAM_CNFG_BITS: 0x%x\n", bam_cnfg_bits); + SPS_DUMP("BAM_TIMER: 0x%x\n", bam_timer); + SPS_DUMP("BAM_TIMER_CTRL: 0x%x\n", bam_timer_ctrl); + + SPS_DUMP("%s", "\nsps: --- BAM-level registers ---\n\n"); +} + +/* output the content of selected BAM pipe registers */ +void print_bam_pipe_selected_reg(void *virt_addr, u32 pipe_index) +{ + void *base = virt_addr; + u32 pipe = pipe_index; + + u32 p_ctrl; + u32 p_sys_mode; + u32 p_direction; + u32 p_lock_group = 0; + + u32 p_irq_en; + u32 p_irq_stts; + u32 p_irq_stts_eot; + u32 p_irq_stts_int; + + u32 p_prd_sdbd; + u32 p_bytes_free; + u32 p_prd_ctrl; + u32 p_prd_toggle; + u32 p_prd_sb_updated; + + u32 p_con_sdbd; + u32 p_bytes_avail; + u32 p_con_ctrl; + u32 p_con_toggle; + u32 p_con_ack_toggle; + u32 p_con_ack_toggle_r; + u32 p_con_wait_4_ack; + u32 p_con_sb_updated; + + u32 p_sw_offset; + u32 p_read_pointer; + u32 p_evnt_reg; + u32 p_write_pointer; + + u32 p_evnt_dest; + u32 p_evnt_dest_msb = 0; + u32 p_desc_fifo_addr; + u32 p_desc_fifo_addr_msb = 0; + u32 p_desc_fifo_size; + u32 p_data_fifo_addr; + u32 p_data_fifo_addr_msb = 0; + u32 p_data_fifo_size; + u32 p_fifo_sizes; + + u32 p_evnt_trd; + u32 p_evnt_trd_val; + + u32 p_retr_ct; + u32 p_retr_offset; + u32 p_si_ct; + u32 p_si_offset; + u32 p_df_ct = 0; + u32 p_df_offset = 0; + u32 p_au_ct1; + u32 p_psm_ct2; + u32 p_psm_ct3; + u32 p_psm_ct3_msb = 0; + u32 p_psm_ct4; + u32 p_psm_ct5; + + u32 p_timer; + u32 p_timer_ctrl; + + if (base == NULL) + return; + + p_ctrl = bam_read_reg(base, P_CTRL, pipe); + p_sys_mode = bam_read_reg_field(base, P_CTRL, pipe, P_SYS_MODE); + p_direction = bam_read_reg_field(base, P_CTRL, pipe, P_DIRECTION); + + p_irq_en = bam_read_reg(base, P_IRQ_EN, pipe); + p_irq_stts = bam_read_reg(base, P_IRQ_STTS, pipe); + p_irq_stts_eot = bam_read_reg_field(base, P_IRQ_STTS, pipe, + P_IRQ_STTS_P_TRNSFR_END_IRQ); + p_irq_stts_int = bam_read_reg_field(base, P_IRQ_STTS, pipe, + P_IRQ_STTS_P_PRCSD_DESC_IRQ); + + p_prd_sdbd = bam_read_reg(base, P_PRDCR_SDBND, pipe); + p_bytes_free = bam_read_reg_field(base, P_PRDCR_SDBND, pipe, + P_PRDCR_SDBNDn_BAM_P_BYTES_FREE); + p_prd_ctrl = bam_read_reg_field(base, P_PRDCR_SDBND, pipe, + P_PRDCR_SDBNDn_BAM_P_CTRL); + p_prd_toggle = bam_read_reg_field(base, P_PRDCR_SDBND, pipe, + P_PRDCR_SDBNDn_BAM_P_TOGGLE); + p_prd_sb_updated = bam_read_reg_field(base, P_PRDCR_SDBND, pipe, + P_PRDCR_SDBNDn_BAM_P_SB_UPDATED); + p_con_sdbd = bam_read_reg(base, P_CNSMR_SDBND, pipe); + p_bytes_avail = bam_read_reg_field(base, P_CNSMR_SDBND, pipe, + P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL); + p_con_ctrl = bam_read_reg_field(base, P_CNSMR_SDBND, pipe, + P_CNSMR_SDBNDn_BAM_P_CTRL); + p_con_toggle = bam_read_reg_field(base, P_CNSMR_SDBND, pipe, + P_CNSMR_SDBNDn_BAM_P_TOGGLE); + p_con_ack_toggle = bam_read_reg_field(base, P_CNSMR_SDBND, pipe, + P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE); + p_con_ack_toggle_r = bam_read_reg_field(base, P_CNSMR_SDBND, pipe, + P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE_R); + p_con_wait_4_ack = bam_read_reg_field(base, P_CNSMR_SDBND, pipe, + P_CNSMR_SDBNDn_BAM_P_WAIT_4_ACK); + p_con_sb_updated = bam_read_reg_field(base, P_CNSMR_SDBND, pipe, + P_CNSMR_SDBNDn_BAM_P_SB_UPDATED); + + p_sw_offset = bam_read_reg(base, P_SW_OFSTS, pipe); + p_read_pointer = bam_read_reg_field(base, P_SW_OFSTS, pipe, + SW_DESC_OFST); + p_evnt_reg = bam_read_reg(base, P_EVNT_REG, pipe); + p_write_pointer = bam_read_reg_field(base, P_EVNT_REG, pipe, + P_DESC_FIFO_PEER_OFST); + + p_evnt_dest = bam_read_reg(base, P_EVNT_DEST_ADDR, pipe); + p_desc_fifo_addr = bam_read_reg(base, P_DESC_FIFO_ADDR, pipe); + p_desc_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES, pipe, + P_DESC_FIFO_SIZE); + p_data_fifo_addr = bam_read_reg(base, P_DATA_FIFO_ADDR, pipe); + p_data_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES, pipe, + P_DATA_FIFO_SIZE); + p_fifo_sizes = bam_read_reg(base, P_FIFO_SIZES, pipe); + + p_evnt_trd = bam_read_reg(base, P_EVNT_GEN_TRSHLD, pipe); + p_evnt_trd_val = bam_read_reg_field(base, P_EVNT_GEN_TRSHLD, pipe, + P_EVNT_GEN_TRSHLD_P_TRSHLD); + + p_retr_ct = bam_read_reg(base, P_RETR_CNTXT, pipe); + p_retr_offset = bam_read_reg_field(base, P_RETR_CNTXT, pipe, + P_RETR_CNTXT_RETR_DESC_OFST); + p_si_ct = bam_read_reg(base, P_SI_CNTXT, pipe); + p_si_offset = bam_read_reg_field(base, P_SI_CNTXT, pipe, + P_SI_CNTXT_SI_DESC_OFST); + p_au_ct1 = bam_read_reg(base, P_AU_PSM_CNTXT_1, pipe); + p_psm_ct2 = bam_read_reg(base, P_PSM_CNTXT_2, pipe); + p_psm_ct3 = bam_read_reg(base, P_PSM_CNTXT_3, pipe); + p_psm_ct4 = bam_read_reg(base, P_PSM_CNTXT_4, pipe); + p_psm_ct5 = bam_read_reg(base, P_PSM_CNTXT_5, pipe); + + p_timer = bam_read_reg(base, P_TIMER, pipe); + p_timer_ctrl = bam_read_reg(base, P_TIMER_CTRL, pipe); + +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM + p_evnt_dest_msb = SPS_LPAE ? + bam_read_reg(base, P_EVNT_DEST_ADDR_MSB, pipe) : 0; + + p_desc_fifo_addr_msb = SPS_LPAE ? + bam_read_reg(base, P_DESC_FIFO_ADDR_MSB, pipe) : 0; + p_data_fifo_addr_msb = SPS_LPAE ? + bam_read_reg(base, P_DATA_FIFO_ADDR_MSB, pipe) : 0; + + p_psm_ct3_msb = SPS_LPAE ? bam_read_reg(base, P_PSM_CNTXT_3, pipe) : 0; + p_lock_group = bam_read_reg_field(base, P_CTRL, pipe, P_LOCK_GROUP); + p_df_ct = bam_read_reg(base, P_DF_CNTXT, pipe); + p_df_offset = bam_read_reg_field(base, P_DF_CNTXT, pipe, + P_DF_CNTXT_DF_DESC_OFST); +#endif + + SPS_DUMP("\nsps: --- Registers of Pipe %d ---\n\n", pipe); + + SPS_DUMP("BAM_P_CTRL: 0x%x\n", p_ctrl); + SPS_DUMP(" SYS_MODE: %d\n", p_sys_mode); + if (p_direction) + SPS_DUMP(" DIRECTION:%d->Producer\n", p_direction); + else + SPS_DUMP(" DIRECTION:%d->Consumer\n", p_direction); + SPS_DUMP(" LOCK_GROUP: 0x%x (%d)\n", p_lock_group, p_lock_group); + + SPS_DUMP("BAM_P_IRQ_EN: 0x%x\n", p_irq_en); + SPS_DUMP("BAM_P_IRQ_STTS: 0x%x\n", p_irq_stts); + SPS_DUMP(" TRNSFR_END_IRQ(EOT): 0x%x\n", p_irq_stts_eot); + SPS_DUMP(" PRCSD_DESC_IRQ(INT): 0x%x\n", p_irq_stts_int); + + SPS_DUMP("BAM_P_PRDCR_SDBND: 0x%x\n", p_prd_sdbd); + SPS_DUMP(" BYTES_FREE: 0x%x (%d)\n", p_bytes_free, p_bytes_free); + SPS_DUMP(" CTRL: 0x%x\n", p_prd_ctrl); + SPS_DUMP(" TOGGLE: %d\n", p_prd_toggle); + SPS_DUMP(" SB_UPDATED: %d\n", p_prd_sb_updated); + SPS_DUMP("BAM_P_CNSMR_SDBND: 0x%x\n", p_con_sdbd); + SPS_DUMP(" WAIT_4_ACK: %d\n", p_con_wait_4_ack); + SPS_DUMP(" BYTES_AVAIL: 0x%x (%d)\n", p_bytes_avail, p_bytes_avail); + SPS_DUMP(" CTRL: 0x%x\n", p_con_ctrl); + SPS_DUMP(" TOGGLE: %d\n", p_con_toggle); + SPS_DUMP(" ACK_TOGGLE: %d\n", p_con_ack_toggle); + SPS_DUMP(" ACK_TOGGLE_R: %d\n", p_con_ack_toggle_r); + SPS_DUMP(" SB_UPDATED: %d\n", p_con_sb_updated); + + SPS_DUMP("BAM_P_SW_DESC_OFST: 0x%x\n", p_sw_offset); + SPS_DUMP(" SW_DESC_OFST: 0x%x\n", p_read_pointer); + SPS_DUMP("BAM_P_EVNT_REG: 0x%x\n", p_evnt_reg); + SPS_DUMP(" DESC_FIFO_PEER_OFST: 0x%x\n", p_write_pointer); + + SPS_DUMP("BAM_P_RETR_CNTXT: 0x%x\n", p_retr_ct); + SPS_DUMP(" RETR_OFFSET: 0x%x\n", p_retr_offset); + SPS_DUMP("BAM_P_SI_CNTXT: 0x%x\n", p_si_ct); + SPS_DUMP(" SI_OFFSET: 0x%x\n", p_si_offset); + SPS_DUMP("BAM_P_DF_CNTXT: 0x%x\n", p_df_ct); + SPS_DUMP(" DF_OFFSET: 0x%x\n", p_df_offset); + + SPS_DUMP("BAM_P_DESC_FIFO_ADDR: 0x%x\n", p_desc_fifo_addr); + SPS_DUMP("BAM_P_DESC_FIFO_ADDR_MSB: 0x%x\n", p_desc_fifo_addr_msb); + SPS_DUMP("BAM_P_DATA_FIFO_ADDR: 0x%x\n", p_data_fifo_addr); + SPS_DUMP("BAM_P_DATA_FIFO_ADDR_MSB: 0x%x\n", p_data_fifo_addr_msb); + SPS_DUMP("BAM_P_FIFO_SIZES: 0x%x\n", p_fifo_sizes); + SPS_DUMP(" DESC_FIFO_SIZE: 0x%x (%d)\n", p_desc_fifo_size, + p_desc_fifo_size); + SPS_DUMP(" DATA_FIFO_SIZE: 0x%x (%d)\n", p_data_fifo_size, + p_data_fifo_size); + + SPS_DUMP("BAM_P_EVNT_DEST_ADDR: 0x%x\n", p_evnt_dest); + SPS_DUMP("BAM_P_EVNT_DEST_ADDR_MSB: 0x%x\n", p_evnt_dest_msb); + SPS_DUMP("BAM_P_EVNT_GEN_TRSHLD: 0x%x\n", p_evnt_trd); + SPS_DUMP(" EVNT_GEN_TRSHLD: 0x%x (%d)\n", p_evnt_trd_val, + p_evnt_trd_val); + + SPS_DUMP("BAM_P_AU_PSM_CNTXT_1: 0x%x\n", p_au_ct1); + SPS_DUMP("BAM_P_PSM_CNTXT_2: 0x%x\n", p_psm_ct2); + SPS_DUMP("BAM_P_PSM_CNTXT_3: 0x%x\n", p_psm_ct3); + SPS_DUMP("BAM_P_PSM_CNTXT_3_MSB: 0x%x\n", p_psm_ct3_msb); + SPS_DUMP("BAM_P_PSM_CNTXT_4: 0x%x\n", p_psm_ct4); + SPS_DUMP("BAM_P_PSM_CNTXT_5: 0x%x\n", p_psm_ct5); + SPS_DUMP("BAM_P_TIMER: 0x%x\n", p_timer); + SPS_DUMP("BAM_P_TIMER_CTRL: 0x%x\n", p_timer_ctrl); + + SPS_DUMP("\nsps: --- Registers of Pipe %d ---\n\n", pipe); +} + +/* output descriptor FIFO of a pipe */ +void print_bam_pipe_desc_fifo(void *virt_addr, u32 pipe_index, u32 option) +{ + void *base = virt_addr; + u32 pipe = pipe_index; + u32 desc_fifo_addr; + u32 desc_fifo_size; + u32 *desc_fifo; + int i; + char desc_info[MAX_MSG_LEN]; + + if (base == NULL) + return; + + desc_fifo_addr = bam_read_reg(base, P_DESC_FIFO_ADDR, pipe); + desc_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES, pipe, + P_DESC_FIFO_SIZE); + + if (desc_fifo_addr == 0) { + SPS_ERR(sps, "sps: desc FIFO address of Pipe %d is NULL\n", + pipe); + return; + } else if (desc_fifo_size == 0) { + SPS_ERR(sps, "sps: desc FIFO size of Pipe %d is 0\n", pipe); + return; + } + + SPS_DUMP("\nsps: --- descriptor FIFO of Pipe %d -----\n\n", + pipe); + + SPS_DUMP("BAM_P_DESC_FIFO_ADDR: 0x%x\n" + "BAM_P_DESC_FIFO_SIZE: 0x%x (%d)\n\n", + desc_fifo_addr, desc_fifo_size, desc_fifo_size); + + desc_fifo = (u32 *) phys_to_virt(desc_fifo_addr); + + if (option == 100) { + SPS_DUMP("%s", + "----- start of data blocks -----\n"); + for (i = 0; i < desc_fifo_size; i += 8) { + u32 *data_block_vir; + u32 data_block_phy = desc_fifo[i / 4]; + + if (data_block_phy) { + data_block_vir = + (u32 *) phys_to_virt(data_block_phy); + + SPS_DUMP("desc addr:0x%x; data addr:0x%x:\n", + desc_fifo_addr + i, data_block_phy); + SPS_DUMP("0x%x, 0x%x, 0x%x, 0x%x\n", + data_block_vir[0], data_block_vir[1], + data_block_vir[2], data_block_vir[3]); + SPS_DUMP("0x%x, 0x%x, 0x%x, 0x%x\n", + data_block_vir[4], data_block_vir[5], + data_block_vir[6], data_block_vir[7]); + SPS_DUMP("0x%x, 0x%x, 0x%x, 0x%x\n", + data_block_vir[8], data_block_vir[9], + data_block_vir[10], data_block_vir[11]); + SPS_DUMP("0x%x, 0x%x, 0x%x, 0x%x\n\n", + data_block_vir[12], data_block_vir[13], + data_block_vir[14], data_block_vir[15]); + } + } + SPS_DUMP("%s", + "----- end of data blocks -----\n"); + } else if (option) { + u32 size = option * 128; + u32 current_desc = bam_pipe_get_desc_read_offset(base, + pipe_index); + u32 begin = 0; + u32 end = desc_fifo_size; + + if (current_desc > size / 2) + begin = current_desc - size / 2; + + if (desc_fifo_size > current_desc + size / 2) + end = current_desc + size / 2; + + SPS_DUMP("%s", + "------------ begin of partial FIFO ------------\n\n"); + + SPS_DUMP("%s", + "desc addr; desc content; desc flags\n"); + for (i = begin; i < end; i += 0x8) { + u32 offset; + u32 flags = desc_fifo[(i / 4) + 1] >> 16; + + memset(desc_info, 0, sizeof(desc_info)); + offset = scnprintf(desc_info, 40, "0x%x: 0x%x, 0x%x: ", + desc_fifo_addr + i, + desc_fifo[i / 4], desc_fifo[(i / 4) + 1]); + + if (flags & SPS_IOVEC_FLAG_INT) + offset += scnprintf(desc_info + offset, 5, + "INT "); + if (flags & SPS_IOVEC_FLAG_EOT) + offset += scnprintf(desc_info + offset, 5, + "EOT "); + if (flags & SPS_IOVEC_FLAG_EOB) + offset += scnprintf(desc_info + offset, 5, + "EOB "); + if (flags & SPS_IOVEC_FLAG_NWD) + offset += scnprintf(desc_info + offset, 5, + "NWD "); + if (flags & SPS_IOVEC_FLAG_CMD) + offset += scnprintf(desc_info + offset, 5, + "CMD "); + if (flags & SPS_IOVEC_FLAG_LOCK) + offset += scnprintf(desc_info + offset, 5, + "LCK "); + if (flags & SPS_IOVEC_FLAG_UNLOCK) + offset += scnprintf(desc_info + offset, 5, + "UNL "); + if (flags & SPS_IOVEC_FLAG_IMME) + offset += scnprintf(desc_info + offset, 5, + "IMM "); + + SPS_DUMP("%s\n", desc_info); + } + + SPS_DUMP("%s", + "\n------------ end of partial FIFO ------------\n"); + } else { + SPS_DUMP("%s", + "---------------- begin of FIFO ----------------\n\n"); + + for (i = 0; i < desc_fifo_size; i += 0x10) + SPS_DUMP("addr 0x%x: 0x%x, 0x%x, 0x%x, 0x%x\n", + desc_fifo_addr + i, + desc_fifo[i / 4], desc_fifo[(i / 4) + 1], + desc_fifo[(i / 4) + 2], desc_fifo[(i / 4) + 3]); + + SPS_DUMP("%s", + "\n---------------- end of FIFO ----------------\n"); + } + + SPS_DUMP("\nsps: --- descriptor FIFO of Pipe %d -----\n\n", + pipe); +} + +/* output BAM_TEST_BUS_REG with specified TEST_BUS_SEL */ +void print_bam_test_bus_reg(void *base, u32 tb_sel) +{ + u32 i; + u32 test_bus_selection[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x20, 0x21, 0x22, 0x23, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46}; + u32 size = ARRAY_SIZE(test_bus_selection); + + if (base == NULL) + return; + + if (tb_sel) { + SPS_DUMP("\nsps:Specified TEST_BUS_SEL value: 0x%x\n", tb_sel); + bam_write_reg_field(base, TEST_BUS_SEL, 0, BAM_TESTBUS_SEL, + tb_sel); + SPS_DUMP("sps:BAM_TEST_BUS_REG:0x%x for TEST_BUS_SEL:0x%x\n\n", + bam_read_reg(base, TEST_BUS_REG, 0), + bam_read_reg_field(base, TEST_BUS_SEL, 0, + BAM_TESTBUS_SEL)); + } + + SPS_DUMP("%s", "\nsps: --- BAM TEST_BUS dump -----\n\n"); + + /* output other selections */ + for (i = 0; i < size; i++) { + bam_write_reg_field(base, TEST_BUS_SEL, 0, BAM_TESTBUS_SEL, + test_bus_selection[i]); + + SPS_DUMP("sps:TEST_BUS_REG:0x%x\t TEST_BUS_SEL:0x%x\n", + bam_read_reg(base, TEST_BUS_REG, 0), + bam_read_reg_field(base, TEST_BUS_SEL, 0, + BAM_TESTBUS_SEL)); + } + + SPS_DUMP("%s", "\nsps: --- BAM TEST_BUS dump -----\n\n"); +} diff --git a/drivers/soc/qcom/sps/bam.h b/drivers/soc/qcom/sps/bam.h new file mode 100644 index 000000000000..4305251689c9 --- /dev/null +++ b/drivers/soc/qcom/sps/bam.h @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/* Bus-Access-Manager (BAM) Hardware manager functions API. */ + +#ifndef _BAM_H_ +#define _BAM_H_ + +#include +#include +#include +#include "spsi.h" + +/* Pipe mode */ +enum bam_pipe_mode { + BAM_PIPE_MODE_BAM2BAM = 0, /* BAM to BAM */ + BAM_PIPE_MODE_SYSTEM = 1, /* BAM to/from System Memory */ +}; + +/* Pipe direction */ +enum bam_pipe_dir { + /* The Pipe Reads data from data-fifo or system-memory */ + BAM_PIPE_CONSUMER = 0, + /* The Pipe Writes data to data-fifo or system-memory */ + BAM_PIPE_PRODUCER = 1, +}; + +/* Stream mode Type */ +enum bam_stream_mode { + BAM_STREAM_MODE_DISABLE = 0, + BAM_STREAM_MODE_ENABLE = 1, +}; + +/* NWD written Type */ +enum bam_write_nwd { + BAM_WRITE_NWD_DISABLE = 0, + BAM_WRITE_NWD_ENABLE = 1, +}; + + +/* Enable Type */ +enum bam_enable { + BAM_DISABLE = 0, + BAM_ENABLE = 1, +}; + +/* Pipe timer mode */ +enum bam_pipe_timer_mode { + BAM_PIPE_TIMER_ONESHOT = 0, + BAM_PIPE_TIMER_PERIODIC = 1, +}; + +struct transfer_descriptor { + u32 addr; /* Buffer physical address */ + u32 size:16; /* Buffer size in bytes */ + u32 flags:16; /* Flag bitmask (see SPS_IOVEC_FLAG_ #defines) */ +} __packed; + +/* BAM pipe initialization parameters */ +struct bam_pipe_parameters { + u16 event_threshold; + u32 pipe_irq_mask; + enum bam_pipe_dir dir; + enum bam_pipe_mode mode; + enum bam_write_nwd write_nwd; + phys_addr_t desc_base; /* Physical address of descriptor FIFO */ + u32 desc_size; /* Size (bytes) of descriptor FIFO */ + u32 lock_group; /* The lock group this pipe belongs to */ + enum bam_stream_mode stream_mode; + u32 ee; /* BAM execution environment index */ + + /* The following are only valid if mode is BAM2BAM */ + u32 peer_phys_addr; + u32 peer_pipe; + phys_addr_t data_base; /* Physical address of data FIFO */ + u32 data_size; /* Size (bytes) of data FIFO */ + bool dummy_peer; +}; + +/** + * Initialize a BAM device + * + * This function initializes a BAM device. + * + * @base - BAM virtual base address. + * + * @ee - BAM execution environment index + * + * @summing_threshold - summing threshold (global for all pipes) + * + * @irq_mask - error interrupts mask + * + * @version - return BAM hardware version + * + * @num_pipes - return number of pipes + * + * @options - BAM configuration options + * + * @return 0 on success, negative value on error + * + */ +int bam_init(void *base, + u32 ee, + u16 summing_threshold, + u32 irq_mask, u32 *version, + u32 *num_pipes, u32 options); + +/** + * Initialize BAM device security execution environment + * + * @base - BAM virtual base address. + * + * @ee - BAM execution environment index + * + * @vmid - virtual master identifier + * + * @pipe_mask - bit mask of pipes to assign to EE + * + * @return 0 on success, negative value on error + * + */ +int bam_security_init(void *base, u32 ee, u32 vmid, u32 pipe_mask); + +/** + * Check a BAM device + * + * This function verifies that a BAM device is enabled and gathers + * the hardware configuration. + * + * @base - BAM virtual base address. + * + * @version - return BAM hardware version + * + * @ee - BAM execution environment index + * + * @num_pipes - return number of pipes + * + * @return 0 on success, negative value on error + * + */ +int bam_check(void *base, u32 *version, u32 ee, u32 *num_pipes); + +/** + * Disable a BAM device + * + * This function disables a BAM device. + * + * @base - BAM virtual base address. + * + * @ee - BAM execution environment index + * + */ +void bam_exit(void *base, u32 ee); + +/** + * This function prints BAM register content + * including TEST_BUS and PIPE register content. + * + * @base - BAM virtual base address. + * + * @ee - BAM execution environment index + */ +void bam_output_register_content(void *base, u32 ee); + + +/** + * Get BAM IRQ source and clear global IRQ status + * + * This function gets BAM IRQ source. + * Clear global IRQ status if it is non-zero. + * + * @base - BAM virtual base address. + * + * @ee - BAM execution environment index + * + * @mask - active pipes mask. + * + * @case - callback case. + * + * @return IRQ status + * + */ +u32 bam_check_irq_source(void *base, u32 ee, u32 mask, + enum sps_callback_case *cb_case); + + +/** + * Initialize a BAM pipe + * + * This function initializes a BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @param - bam pipe parameters. + * + * @ee - BAM execution environment index + * + * @return 0 on success, negative value on error + * + */ +int bam_pipe_init(void *base, u32 pipe, struct bam_pipe_parameters *param, + u32 ee); + +/** + * Reset the BAM pipe + * + * This function resets the BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @ee - BAM execution environment index + * + */ +void bam_pipe_exit(void *base, u32 pipe, u32 ee); + +/** + * Enable a BAM pipe + * + * This function enables a BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + */ +void bam_pipe_enable(void *base, u32 pipe); + +/** + * Disable a BAM pipe + * + * This function disables a BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + */ +void bam_pipe_disable(void *base, u32 pipe); + +/** + * Get a BAM pipe enable state + * + * This function determines if a BAM pipe is enabled. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @return true if enabled, false if disabled + * + */ +int bam_pipe_is_enabled(void *base, u32 pipe); + +/** + * Configure interrupt for a BAM pipe + * + * This function configures the interrupt for a BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @irq_en - enable or disable interrupt + * + * @src_mask - interrupt source mask, set regardless of whether + * interrupt is disabled + * + * @ee - BAM execution environment index + * + */ +void bam_pipe_set_irq(void *base, u32 pipe, enum bam_enable irq_en, + u32 src_mask, u32 ee); + +/** + * Configure a BAM pipe for satellite MTI use + * + * This function configures a BAM pipe for satellite MTI use. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @irq_gen_addr - physical address written to generate MTI + * + * @ee - BAM execution environment index + * + */ +void bam_pipe_satellite_mti(void *base, u32 pipe, u32 irq_gen_addr, u32 ee); + +/** + * Configure MTI for a BAM pipe + * + * This function configures the interrupt for a BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @irq_en - enable or disable interrupt + * + * @src_mask - interrupt source mask, set regardless of whether + * interrupt is disabled + * + * @irq_gen_addr - physical address written to generate MTI + * + */ +void bam_pipe_set_mti(void *base, u32 pipe, enum bam_enable irq_en, + u32 src_mask, u32 irq_gen_addr); + +/** + * Get and Clear BAM pipe IRQ status + * + * This function gets and clears BAM pipe IRQ status. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @return IRQ status + * + */ +u32 bam_pipe_get_and_clear_irq_status(void *base, u32 pipe); + +/** + * Set write offset for a BAM pipe + * + * This function sets the write offset for a BAM pipe. This is + * the offset that is maintained by software in system mode. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @next_write - descriptor FIFO write offset + * + */ +void bam_pipe_set_desc_write_offset(void *base, u32 pipe, u32 next_write); + +/** + * Get write offset for a BAM pipe + * + * This function gets the write offset for a BAM pipe. This is + * the offset that is maintained by the pipe's peer pipe or by software. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @return descriptor FIFO write offset + * + */ +u32 bam_pipe_get_desc_write_offset(void *base, u32 pipe); + +/** + * Get read offset for a BAM pipe + * + * This function gets the read offset for a BAM pipe. This is + * the offset that is maintained by the pipe in system mode. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @return descriptor FIFO read offset + * + */ +u32 bam_pipe_get_desc_read_offset(void *base, u32 pipe); + +/** + * Configure inactivity timer count for a BAM pipe + * + * This function configures the inactivity timer count for a BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @mode - timer operating mode + * + * @timeout_count - timeout count + * + */ +void bam_pipe_timer_config(void *base, u32 pipe, + enum bam_pipe_timer_mode mode, + u32 timeout_count); + +/** + * Reset inactivity timer for a BAM pipe + * + * This function resets the inactivity timer count for a BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + */ +void bam_pipe_timer_reset(void *base, u32 pipe); + +/** + * Get inactivity timer count for a BAM pipe + * + * This function gets the inactivity timer count for a BAM pipe. + * + * @base - BAM virtual base address. + * + * @pipe - pipe index + * + * @return inactivity timer count + * + */ +u32 bam_pipe_timer_get_count(void *base, u32 pipe); + +/* + * bam_pipe_check_zlt - Check if the last desc is ZLT. + * @base: BAM virtual address + * @pipe: pipe index + * + * This function checks if the last desc in the desc FIFO is a ZLT desc. + * + * @return true if the last desc in the desc FIFO is a ZLT desc. Otherwise + * return false. + */ +bool bam_pipe_check_zlt(void *base, u32 pipe); + +/* + * bam_pipe_check_pipe_empty - Check if desc FIFO is empty. + * @base: BAM virtual address + * @pipe: pipe index + * + * This function checks if the desc FIFO of this pipe is empty. + * + * @return true if desc FIFO is empty. Otherwise return false. + */ +bool bam_pipe_check_pipe_empty(void *base, u32 pipe); +#endif /* _BAM_H_ */ diff --git a/drivers/soc/qcom/sps/sps.c b/drivers/soc/qcom/sps/sps.c new file mode 100644 index 000000000000..d94b4f5b073f --- /dev/null +++ b/drivers/soc/qcom/sps/sps.c @@ -0,0 +1,2938 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/* Smart-Peripheral-Switch (SPS) Module. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sps_bam.h" +#include "spsi.h" +#include "sps_core.h" + +#define SPS_DRV_NAME "msm_sps" /* must match the platform_device name */ + +/** + * SPS driver state + */ +struct sps_drv *sps; + +u32 d_type; +bool enhd_pipe; +bool imem; +enum sps_bam_type bam_type; +static enum sps_bam_type bam_types[] = { + SPS_BAM_LEGACY, SPS_BAM_NDP, SPS_BAM_NDP_4K}; + +static void sps_device_de_init(void); + +#ifdef CONFIG_DEBUG_FS +u8 debugfs_record_enabled; +u8 logging_option; +u8 debug_level_option; +u8 print_limit_option; +static u8 reg_dump_option; +static u32 testbus_sel; +static u32 bam_pipe_sel; +static u32 desc_option; +/* + * Specifies range of log level from level 0 to level 3 to have fine-granularity + * for logging to serve all BAM use cases. + */ +static u32 log_level_sel; + +static char *debugfs_buf; +static u32 debugfs_buf_size; +static u32 debugfs_buf_used; +static int wraparound; +static struct mutex sps_debugfs_lock; + +static struct dentry *dent; +static struct dentry *dfile_info; +static struct dentry *dfile_logging_option; +static struct dentry *dfile_bam_addr; + +static struct sps_bam *phy2bam(phys_addr_t phys_addr); + +/* record debug info for debugfs */ +void sps_debugfs_record(const char *msg) +{ + mutex_lock(&sps_debugfs_lock); + if (debugfs_record_enabled) { + if (debugfs_buf_used + MAX_MSG_LEN >= debugfs_buf_size) { + debugfs_buf_used = 0; + wraparound = true; + } + debugfs_buf_used += scnprintf(debugfs_buf + debugfs_buf_used, + debugfs_buf_size - debugfs_buf_used, msg); + + if (wraparound) + scnprintf(debugfs_buf + debugfs_buf_used, + debugfs_buf_size - debugfs_buf_used, + "\n**** end line of sps log ****\n\n"); + } + mutex_unlock(&sps_debugfs_lock); +} + +/* read the recorded debug info to userspace */ +static ssize_t sps_read_info(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret = 0; + int size; + + mutex_lock(&sps_debugfs_lock); + if (debugfs_record_enabled) { + if (wraparound) + size = debugfs_buf_size - MAX_MSG_LEN; + else + size = debugfs_buf_used; + + ret = simple_read_from_buffer(ubuf, count, ppos, + debugfs_buf, size); + } + mutex_unlock(&sps_debugfs_lock); + + return ret; +} + +/* + * set the buffer size (in KB) for debug info + */ +static ssize_t sps_set_info(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long missing; + char str[MAX_MSG_LEN]; + int i; + u32 buf_size_kb = 0; + u32 new_buf_size; + u32 size = sizeof(str) < count ? sizeof(str) : count; + + memset(str, 0, sizeof(str)); + missing = copy_from_user(str, buf, size); + if (missing) + return -EFAULT; + + for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + buf_size_kb = (buf_size_kb * 10) + (str[i] - '0'); + + pr_info("sps:debugfs: input buffer size is %dKB\n", buf_size_kb); + + if ((logging_option == 0) || (logging_option == 2)) { + pr_info("sps:debugfs: need to first turn on recording\n"); + return -EFAULT; + } + + if (buf_size_kb < 1) { + pr_info("sps:debugfs:buffer size should be no less than 1KB\n"); + return -EFAULT; + } + + if (buf_size_kb > (INT_MAX/SZ_1K)) { + pr_err("sps:debugfs: buffer size is too large\n"); + return -EFAULT; + } + + new_buf_size = buf_size_kb * SZ_1K; + + mutex_lock(&sps_debugfs_lock); + if (debugfs_record_enabled) { + if (debugfs_buf_size == new_buf_size) { + /* need do nothing */ + pr_info( + "sps:debugfs: input buffer size is the same as before\n" + ); + mutex_unlock(&sps_debugfs_lock); + return count; + } + /* release the current buffer */ + debugfs_record_enabled = false; + debugfs_buf_used = 0; + wraparound = false; + kfree(debugfs_buf); + debugfs_buf = NULL; + } + + /* allocate new buffer */ + debugfs_buf_size = new_buf_size; + + debugfs_buf = kzalloc(debugfs_buf_size, GFP_KERNEL); + if (!debugfs_buf) { + debugfs_buf_size = 0; + mutex_unlock(&sps_debugfs_lock); + return -ENOMEM; + } + + debugfs_buf_used = 0; + wraparound = false; + debugfs_record_enabled = true; + mutex_unlock(&sps_debugfs_lock); + + return count; +} + +static const struct file_operations sps_info_ops = { + .read = sps_read_info, + .write = sps_set_info, +}; + +/* return the current logging option to userspace */ +static ssize_t sps_read_logging_option(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char value[MAX_MSG_LEN]; + int nbytes; + + nbytes = scnprintf(value, MAX_MSG_LEN, "%d\n", logging_option); + + return simple_read_from_buffer(ubuf, count, ppos, value, nbytes); +} + +/* + * set the logging option + */ +static ssize_t sps_set_logging_option(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret; + u32 option = 0; + + ret = kstrtouint_from_user(buf, count, 10, &option); + if (ret) + return ret; + + pr_info("sps:debugfs: try to change logging option to %d\n", option); + + if (option > 3) { + pr_err("sps:debugfs: invalid logging option:%d\n", option); + return count; + } + + mutex_lock(&sps_debugfs_lock); + if (((option == 0) || (option == 2)) && + ((logging_option == 1) || (logging_option == 3))) { + debugfs_record_enabled = false; + kfree(debugfs_buf); + debugfs_buf = NULL; + debugfs_buf_used = 0; + debugfs_buf_size = 0; + wraparound = false; + } + + logging_option = option; + mutex_unlock(&sps_debugfs_lock); + + return count; +} + +static const struct file_operations sps_logging_option_ops = { + .read = sps_read_logging_option, + .write = sps_set_logging_option, +}; + +/* + * input the bam physical address + */ +static ssize_t sps_set_bam_addr(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + u32 ret; + int i; + u32 bam_addr = 0; + struct sps_bam *bam; + u32 num_pipes = 0; + void *vir_addr; + + ret = kstrtouint_from_user(buf, count, 10, &bam_addr); + if (ret) + return ret; + + pr_info("sps:debugfs:input BAM physical address:0x%x\n", bam_addr); + + bam = phy2bam(bam_addr); + + if (bam == NULL) { + pr_err("sps:debugfs:BAM 0x%x is not registered\n", bam_addr); + return count; + } + vir_addr = &bam->base; + num_pipes = bam->props.num_pipes; + if (log_level_sel <= SPS_IPC_MAX_LOGLEVEL) + bam->ipc_loglevel = log_level_sel; + + switch (reg_dump_option) { + case 1: /* output all registers of this BAM */ + print_bam_reg(bam->base); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_reg(bam->base, i); + break; + case 2: /* output BAM-level registers */ + print_bam_reg(bam->base); + break; + case 3: /* output selected BAM-level registers */ + print_bam_selected_reg(vir_addr, bam->props.ee); + break; + case 4: /* output selected registers of all pipes */ + for (i = 0; i < num_pipes; i++) + print_bam_pipe_selected_reg(vir_addr, i); + break; + case 5: /* output selected registers of selected pipes */ + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + break; + case 6: /* output selected registers of typical pipes */ + print_bam_pipe_selected_reg(vir_addr, 4); + print_bam_pipe_selected_reg(vir_addr, 5); + break; + case 7: /* output desc FIFO of all pipes */ + for (i = 0; i < num_pipes; i++) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + break; + case 8: /* output desc FIFO of selected pipes */ + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + break; + case 9: /* output desc FIFO of typical pipes */ + print_bam_pipe_desc_fifo(vir_addr, 4, 0); + print_bam_pipe_desc_fifo(vir_addr, 5, 0); + break; + case 10: /* output selected registers and desc FIFO of all pipes */ + for (i = 0; i < num_pipes; i++) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, 0); + } + break; + case 11: /* output selected registers and desc FIFO of selected pipes */ + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, 0); + } + break; + case 12: /* output selected registers and desc FIFO of typical pipes */ + print_bam_pipe_selected_reg(vir_addr, 4); + print_bam_pipe_desc_fifo(vir_addr, 4, 0); + print_bam_pipe_selected_reg(vir_addr, 5); + print_bam_pipe_desc_fifo(vir_addr, 5, 0); + break; + case 13: /* output BAM_TEST_BUS_REG */ + if (testbus_sel) + print_bam_test_bus_reg(vir_addr, testbus_sel); + else { + pr_info("sps:output TEST_BUS_REG for all TEST_BUS_SEL\n"); + print_bam_test_bus_reg(vir_addr, testbus_sel); + } + break; + case 14: /* output partial desc FIFO of selected pipes */ + if (desc_option == 0) + desc_option = 1; + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, + desc_option); + break; + case 15: /* output partial data blocks of descriptors */ + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 100); + break; + case 16: /* output all registers of selected pipes */ + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_reg(bam->base, i); + break; + case 91: /* + * output testbus register, BAM global regisers + * and registers of all pipes + */ + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_selected_reg(vir_addr, i); + break; + case 92: /* + * output testbus register, BAM global regisers + * and registers of selected pipes + */ + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + break; + case 93: /* + * output registers and partial desc FIFOs + * of selected pipes: format 1 + */ + if (desc_option == 0) + desc_option = 1; + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, + desc_option); + break; + case 94: /* + * output registers and partial desc FIFOs + * of selected pipes: format 2 + */ + if (desc_option == 0) + desc_option = 1; + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, + desc_option); + } + break; + case 95: /* + * output registers and desc FIFOs + * of selected pipes: format 1 + */ + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + break; + case 96: /* + * output registers and desc FIFOs + * of selected pipes: format 2 + */ + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, 0); + } + break; + case 97: /* + * output registers, desc FIFOs and partial data blocks + * of selected pipes: format 1 + */ + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 100); + break; + case 98: /* + * output registers, desc FIFOs and partial data blocks + * of selected pipes: format 2 + */ + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (bam_pipe_sel & (1UL << i)) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, 0); + print_bam_pipe_desc_fifo(vir_addr, i, 100); + } + break; + case 99: /* output all registers, desc FIFOs and partial data blocks */ + print_bam_test_bus_reg(vir_addr, testbus_sel); + print_bam_reg(bam->base); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_reg(bam->base, i); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_selected_reg(vir_addr, i); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_desc_fifo(vir_addr, i, 100); + break; + default: + pr_info("sps:no dump option is chosen yet\n"); + } + + return count; +} + +static const struct file_operations sps_bam_addr_ops = { + .write = sps_set_bam_addr, +}; + +static void sps_debugfs_init(void) +{ + debugfs_record_enabled = false; + logging_option = 0; + debug_level_option = 0; + print_limit_option = 0; + reg_dump_option = 0; + testbus_sel = 0; + bam_pipe_sel = 0; + desc_option = 0; + debugfs_buf_size = 0; + debugfs_buf_used = 0; + wraparound = false; + log_level_sel = SPS_IPC_MAX_LOGLEVEL + 1; + + dent = debugfs_create_dir("sps", NULL); + if (IS_ERR(dent)) { + pr_err("sps:fail to create the folder for debug_fs\n"); + return; + } + + dfile_info = debugfs_create_file("info", 0664, dent, NULL, + &sps_info_ops); + if (!dfile_info || IS_ERR(dfile_info)) { + pr_err("sps:fail to create the file for debug_fs info\n"); + goto cleanup; + } + + dfile_logging_option = debugfs_create_file("logging_option", 0664, + dent, NULL, &sps_logging_option_ops); + if (!dfile_logging_option || IS_ERR(dfile_logging_option)) { + pr_err("sps:fail to create debug_fs for logging_option\n"); + goto cleanup; + } + + debugfs_create_u8("debug_level_option", + 0664, dent, &debug_level_option); + + debugfs_create_u8("print_limit_option", + 0664, dent, &print_limit_option); + + debugfs_create_u8("reg_dump_option", 0664, dent, ®_dump_option); + + debugfs_create_u32("testbus_sel", 0664, dent, &testbus_sel); + + debugfs_create_u32("bam_pipe_sel", 0664, dent, &bam_pipe_sel); + + debugfs_create_u32("desc_option", 0664, dent, &desc_option); + + dfile_bam_addr = debugfs_create_file("bam_addr", 0664, + dent, NULL, &sps_bam_addr_ops); + if (!dfile_bam_addr || IS_ERR(dfile_bam_addr)) { + pr_err("sps:fail to create the file for debug_fs bam_addr\n"); + goto cleanup; + } + + debugfs_create_u32("log_level_sel", 0664, dent, &log_level_sel); + + mutex_init(&sps_debugfs_lock); + + return; + +cleanup: + debugfs_remove_recursive(dent); +} + +static void sps_debugfs_exit(void) +{ + debugfs_remove_recursive(dent); + kfree(debugfs_buf); + debugfs_buf = NULL; +} +#endif + +/* Get the debug info of BAM registers and descriptor FIFOs */ +int sps_get_bam_debug_info(unsigned long dev, u32 option, u32 para, + u32 tb_sel, u32 desc_sel) +{ + int res = 0; + struct sps_bam *bam; + u32 i; + u32 num_pipes = 0; + void *vir_addr; + + if (dev == 0) { + SPS_ERR(sps, "sps: device handle should not be 0\n"); + return SPS_ERROR; + } + + if (sps == NULL || !sps->is_ready) { + SPS_DBG3(sps, "sps: sps driver is not ready\n"); + return -EPROBE_DEFER; + } + + mutex_lock(&sps->lock); + /* Search for the target BAM device */ + bam = sps_h2bam(dev); + if (bam == NULL) { + pr_err("sps:Can't find any BAM with handle 0x%pK\n", + (void *)dev); + mutex_unlock(&sps->lock); + return SPS_ERROR; + } + mutex_unlock(&sps->lock); + + vir_addr = &bam->base; + num_pipes = bam->props.num_pipes; + + SPS_DUMP("sps: dump BAM:%pa\n", &bam->props.phys_addr); + + switch (option) { + case 1: /* output all registers of this BAM */ + print_bam_reg(bam->base); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_reg(bam->base, i); + break; + case 2: /* output BAM-level registers */ + print_bam_reg(bam->base); + break; + case 3: /* output selected BAM-level registers */ + print_bam_selected_reg(vir_addr, bam->props.ee); + break; + case 4: /* output selected registers of all pipes */ + for (i = 0; i < num_pipes; i++) + print_bam_pipe_selected_reg(vir_addr, i); + break; + case 5: /* output selected registers of selected pipes */ + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + break; + case 6: /* output selected registers of typical pipes */ + print_bam_pipe_selected_reg(vir_addr, 4); + print_bam_pipe_selected_reg(vir_addr, 5); + break; + case 7: /* output desc FIFO of all pipes */ + for (i = 0; i < num_pipes; i++) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + break; + case 8: /* output desc FIFO of selected pipes */ + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + break; + case 9: /* output desc FIFO of typical pipes */ + print_bam_pipe_desc_fifo(vir_addr, 4, 0); + print_bam_pipe_desc_fifo(vir_addr, 5, 0); + break; + case 10: /* output selected registers and desc FIFO of all pipes */ + for (i = 0; i < num_pipes; i++) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, 0); + } + break; + case 11: /* output selected registers and desc FIFO of selected pipes */ + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, 0); + } + break; + case 12: /* output selected registers and desc FIFO of typical pipes */ + print_bam_pipe_selected_reg(vir_addr, 4); + print_bam_pipe_desc_fifo(vir_addr, 4, 0); + print_bam_pipe_selected_reg(vir_addr, 5); + print_bam_pipe_desc_fifo(vir_addr, 5, 0); + break; + case 13: /* output BAM_TEST_BUS_REG */ + if (tb_sel) + print_bam_test_bus_reg(vir_addr, tb_sel); + else + pr_info("sps:TEST_BUS_SEL should NOT be zero\n"); + break; + case 14: /* output partial desc FIFO of selected pipes */ + if (desc_sel == 0) + desc_sel = 1; + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, + desc_sel); + break; + case 15: /* output partial data blocks of descriptors */ + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 100); + break; + case 16: /* output all registers of selected pipes */ + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_reg(bam->base, i); + break; + case 91: /* + * output testbus register, BAM global regisers + * and registers of all pipes + */ + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_selected_reg(vir_addr, i); + break; + case 92: /* + * output testbus register, BAM global regisers + * and registers of selected pipes + */ + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + break; + case 93: /* + * output registers and partial desc FIFOs + * of selected pipes: format 1 + */ + if (desc_sel == 0) + desc_sel = 1; + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, + desc_sel); + break; + case 94: /* + * output registers and partial desc FIFOs + * of selected pipes: format 2 + */ + if (desc_sel == 0) + desc_sel = 1; + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, + desc_sel); + } + break; + case 95: /* + * output registers and desc FIFOs + * of selected pipes: format 1 + */ + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + break; + case 96: /* + * output registers and desc FIFOs + * of selected pipes: format 2 + */ + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, 0); + } + break; + case 97: /* + * output registers, desc FIFOs and partial data blocks + * of selected pipes: format 1 + */ + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_selected_reg(vir_addr, i); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) + print_bam_pipe_desc_fifo(vir_addr, i, 100); + break; + case 98: /* + * output registers, desc FIFOs and partial data blocks + * of selected pipes: format 2 + */ + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + if (para & (1UL << i)) { + print_bam_pipe_selected_reg(vir_addr, i); + print_bam_pipe_desc_fifo(vir_addr, i, 0); + print_bam_pipe_desc_fifo(vir_addr, i, 100); + } + break; + case 99: /* output all registers, desc FIFOs and partial data blocks */ + print_bam_test_bus_reg(vir_addr, tb_sel); + print_bam_reg(bam->base); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_reg(bam->base, i); + print_bam_selected_reg(vir_addr, bam->props.ee); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_selected_reg(vir_addr, i); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_desc_fifo(vir_addr, i, 0); + for (i = 0; i < num_pipes; i++) + print_bam_pipe_desc_fifo(vir_addr, i, 100); + break; + default: + pr_info("sps:no option is chosen yet\n"); + } + + return res; +} +EXPORT_SYMBOL(sps_get_bam_debug_info); + +/** + * Initialize SPS device + * + * This function initializes the SPS device. + * + * @return 0 on success, negative value on error + * + */ +static int sps_device_init(void) +{ + int result; + int success; +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + struct sps_bam_props bamdma_props = {0}; +#endif + + SPS_DBG3(sps, "Enter\n"); + + success = false; + + result = sps_mem_init(sps->pipemem_phys_base, sps->pipemem_size); + if (result) { + SPS_ERR(sps, "sps: SPS memory init failed\n"); + goto exit_err; + } + + INIT_LIST_HEAD(&sps->bams_q); + mutex_init(&sps->lock); + + if (sps_rm_init(&sps->connection_ctrl, sps->options)) { + SPS_ERR(sps, "sps: Fail to init SPS resource manager\n"); + goto exit_err; + } + + result = sps_bam_driver_init(sps->options); + if (result) { + SPS_ERR(sps, "sps: SPS BAM driver init failed\n"); + goto exit_err; + } + + /* Initialize the BAM DMA device */ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + bamdma_props.phys_addr = sps->bamdma_bam_phys_base; + bamdma_props.virt_addr = ioremap(sps->bamdma_bam_phys_base, + sps->bamdma_bam_size); + + if (!bamdma_props.virt_addr) { + SPS_ERR(sps, "sps: Fail to IO map BAM-DMA BAM registers\n"); + goto exit_err; + } + + SPS_DBG3(sps, "sps:bamdma_bam.phys=%pa.virt=0x%pK\n", + &bamdma_props.phys_addr, + bamdma_props.virt_addr); + + bamdma_props.periph_phys_addr = sps->bamdma_dma_phys_base; + bamdma_props.periph_virt_size = sps->bamdma_dma_size; + bamdma_props.periph_virt_addr = ioremap(sps->bamdma_dma_phys_base, + sps->bamdma_dma_size); + + if (!bamdma_props.periph_virt_addr) { + SPS_ERR(sps, "sps: Fail to IO map BAM-DMA peripheral reg\n"); + goto exit_err; + } + + SPS_DBG3(sps, "sps:bamdma_dma.phys=%pa.virt=0x%pK\n", + &bamdma_props.periph_phys_addr, + bamdma_props.periph_virt_addr); + + bamdma_props.irq = sps->bamdma_irq; + + bamdma_props.event_threshold = 0x10; /* Pipe event threshold */ + bamdma_props.summing_threshold = 0x10; /* BAM event threshold */ + + bamdma_props.options = SPS_BAM_OPT_BAMDMA; + bamdma_props.restricted_pipes = sps->bamdma_restricted_pipes; + + result = sps_dma_init(&bamdma_props); + if (result) { + SPS_ERR(sps, "sps: SPS BAM DMA driver init failed\n"); + goto exit_err; + } +#endif /* CONFIG_SPS_SUPPORT_BAMDMA */ + + result = sps_map_init(NULL, sps->options); + if (result) { + SPS_ERR(sps, + "sps: SPS connection mapping init failed\n"); + goto exit_err; + } + + success = true; +exit_err: + if (!success) { +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + sps_device_de_init(); +#endif + return SPS_ERROR; + } + + return 0; +} + +/** + * De-initialize SPS device + * + * This function de-initializes the SPS device. + * + * @return 0 on success, negative value on error + * + */ +static void sps_device_de_init(void) +{ + SPS_DBG3(sps, "Enter\n"); + + if (sps != NULL) { +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + sps_dma_de_init(); +#endif + /* Are there any remaining BAM registrations? */ + if (!list_empty(&sps->bams_q)) + SPS_ERR(sps, + "sps: BAMs are still registered\n"); + + sps_map_de_init(); + } + + sps_mem_de_init(); +} + +/** + * Initialize client state context + * + * This function initializes a client state context struct. + * + * @client - Pointer to client state context + * + * @return 0 on success, negative value on error + * + */ +static int sps_client_init(struct sps_pipe *client) +{ + SPS_DBG(sps, "Enter\n"); + + if (client == NULL) + return -EINVAL; + + /* + * NOTE: Cannot store any state within the SPS driver because + * the driver init function may not have been called yet. + */ + memset(client, 0, sizeof(*client)); + sps_rm_config_init(&client->connect); + + client->client_state = SPS_STATE_DISCONNECT; + client->bam = NULL; + + return 0; +} + +/** + * De-initialize client state context + * + * This function de-initializes a client state context struct. + * + * @client - Pointer to client state context + * + * @return 0 on success, negative value on error + * + */ +static int sps_client_de_init(struct sps_pipe *client) +{ + SPS_DBG(sps, "Enter\n"); + + if (client->client_state != SPS_STATE_DISCONNECT) { + SPS_ERR(sps, "sps:De-init client in connected state: 0x%x\n", + client->client_state); + return SPS_ERROR; + } + + client->bam = NULL; + client->map = NULL; + memset(&client->connect, 0, sizeof(client->connect)); + + return 0; +} + +/** + * Find the BAM device from the physical address + * + * This function finds a BAM device in the BAM registration list that + * matches the specified physical address. + * + * @phys_addr - physical address of the BAM + * + * @return - pointer to the BAM device struct, or NULL on error + * + */ +static struct sps_bam *phy2bam(phys_addr_t phys_addr) +{ + struct sps_bam *bam; + + SPS_DBG2(sps, "Enter\n"); + + list_for_each_entry(bam, &sps->bams_q, list) { + if (bam->props.phys_addr == phys_addr) + return bam; + } + + return NULL; +} + +/** + * Find the handle of a BAM device based on the physical address + * + * This function finds a BAM device in the BAM registration list that + * matches the specified physical address, and returns its handle. + * + * @phys_addr - physical address of the BAM + * + * @h - device handle of the BAM + * + * @return 0 on success, negative value on error + * + */ +int sps_phy2h(phys_addr_t phys_addr, unsigned long *handle) +{ + struct sps_bam *bam; + + SPS_DBG2(sps, "Enter\n"); + + if (sps == NULL || !sps->is_ready) { + SPS_DBG3(sps, "sps: sps driver is not ready\n"); + return -EPROBE_DEFER; + } + + if (handle == NULL) { + SPS_ERR(sps, "sps: handle is NULL\n"); + return SPS_ERROR; + } + + list_for_each_entry(bam, &sps->bams_q, list) { + if (bam->props.phys_addr == phys_addr) { + *handle = (uintptr_t) bam; + return 0; + } + } + + SPS_ERR(sps, + "sps: BAM device %pa is not registered yet\n", &phys_addr); + + return -ENODEV; +} +EXPORT_SYMBOL(sps_phy2h); + +/** + * Setup desc/data FIFO for bam-to-bam connection + * + * @mem_buffer - Pointer to struct for allocated memory properties. + * + * @addr - address of FIFO + * + * @size - FIFO size + * + * @use_offset - use address offset instead of absolute address + * + * @return 0 on success, negative value on error + * + */ +int sps_setup_bam2bam_fifo(struct sps_mem_buffer *mem_buffer, + u32 addr, u32 size, int use_offset) +{ + SPS_DBG1(sps, "Enter\n"); + + if ((mem_buffer == NULL) || (size == 0)) { + SPS_ERR(sps, "sps: invalid buffer address or size\n"); + return SPS_ERROR; + } + + if (sps == NULL || !sps->is_ready) { + SPS_DBG3(sps, "sps: sps driver is not ready\n"); + return -EPROBE_DEFER; + } + + if (use_offset) { + if ((addr + size) <= sps->pipemem_size) + mem_buffer->phys_base = sps->pipemem_phys_base + addr; + else { + SPS_ERR(sps, + "sps: requested mem is out of pipe mem range\n"); + return SPS_ERROR; + } + } else { + if (addr >= sps->pipemem_phys_base && + (addr + size) <= (sps->pipemem_phys_base + + sps->pipemem_size)) + mem_buffer->phys_base = addr; + else { + SPS_ERR(sps, + "sps: requested mem is out of pipe mem range\n"); + return SPS_ERROR; + } + } + + mem_buffer->base = spsi_get_mem_ptr(mem_buffer->phys_base); + mem_buffer->size = size; + + memset(mem_buffer->base, 0, mem_buffer->size); + + return 0; +} +EXPORT_SYMBOL(sps_setup_bam2bam_fifo); + +/** + * Find the BAM device from the handle + * + * This function finds a BAM device in the BAM registration list that + * matches the specified device handle. + * + * @h - device handle of the BAM + * + * @return - pointer to the BAM device struct, or NULL on error + * + */ +struct sps_bam *sps_h2bam(unsigned long h) +{ + struct sps_bam *bam; + + SPS_DBG1(sps, "sps: BAM handle:0x%pK\n", (void *)h); + + if (h == SPS_DEV_HANDLE_MEM || h == SPS_DEV_HANDLE_INVALID) + return NULL; + + list_for_each_entry(bam, &sps->bams_q, list) { + if ((uintptr_t) bam == h) + return bam; + } + + SPS_ERR(sps, "sps:Can't find BAM device for handle 0x%pK\n", (void *)h); + + return NULL; +} + +/** + * Lock BAM device + * + * This function obtains the BAM spinlock on the client's connection. + * + * @pipe - pointer to client pipe state + * + * @return pointer to BAM device struct, or NULL on error + * + */ +static struct sps_bam *sps_bam_lock(struct sps_pipe *pipe) +{ + struct sps_bam *bam; + u32 pipe_index; + + bam = pipe->bam; + if (bam == NULL) { + SPS_ERR(sps, "sps: Connection is not in connected state\n"); + return NULL; + } + + spin_lock_irqsave(&bam->connection_lock, bam->irqsave_flags); + + /* Verify client owns this pipe */ + pipe_index = pipe->pipe_index; + if (pipe_index >= bam->props.num_pipes || + pipe != bam->pipes[pipe_index]) { + SPS_ERR(bam, + "sps:Client not owner of BAM %pa pipe: %d (max %d)\n", + &bam->props.phys_addr, pipe_index, + bam->props.num_pipes); + spin_unlock_irqrestore(&bam->connection_lock, + bam->irqsave_flags); + return NULL; + } + + return bam; +} + +/** + * Unlock BAM device + * + * This function releases the BAM spinlock on the client's connection. + * + * @bam - pointer to BAM device struct + * + */ +static inline void sps_bam_unlock(struct sps_bam *bam) +{ + spin_unlock_irqrestore(&bam->connection_lock, bam->irqsave_flags); +} + +/** + * Connect an SPS connection end point + * + */ +int sps_connect(struct sps_pipe *h, struct sps_connect *connect) +{ + struct sps_pipe *pipe = h; + unsigned long dev; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (connect == NULL) { + SPS_ERR(sps, "sps: connection is NULL\n"); + return SPS_ERROR; + } + + if (sps == NULL) + return -ENODEV; + + if (!sps->is_ready) { + SPS_ERR(sps, "sps: sps driver is not ready\n"); + return -EAGAIN; + } + + if ((connect->lock_group != SPSRM_CLEAR) + && (connect->lock_group > BAM_MAX_P_LOCK_GROUP_NUM)) { + SPS_ERR(sps, + "sps: The value of pipe lock group is invalid\n"); + return SPS_ERROR; + } + + mutex_lock(&sps->lock); + /* + * Must lock the BAM device at the top level function, so must + * determine which BAM is the target for the connection + */ + if (connect->mode == SPS_MODE_SRC) + dev = connect->source; + else + dev = connect->destination; + + bam = sps_h2bam(dev); + if (bam == NULL) { + SPS_ERR(sps, "sps:Invalid BAM device handle: 0x%pK\n", + (void *)dev); + result = SPS_ERROR; + goto exit_err; + } + + mutex_lock(&bam->lock); + SPS_DBG2(bam, "sps: bam %pa src 0x%pK dest 0x%pK mode %s\n", + BAM_ID(bam), + (void *)connect->source, + (void *)connect->destination, + connect->mode == SPS_MODE_SRC ? "SRC" : "DEST"); + + /* Allocate resources for the specified connection */ + pipe->connect = *connect; + result = sps_rm_state_change(pipe, SPS_STATE_ALLOCATE); + if (result) { + mutex_unlock(&bam->lock); + goto exit_err; + } + + /* Configure the connection */ + result = sps_rm_state_change(pipe, SPS_STATE_CONNECT); + mutex_unlock(&bam->lock); + if (result) { + sps_disconnect(h); + goto exit_err; + } + +exit_err: + mutex_unlock(&sps->lock); + + return result; +} +EXPORT_SYMBOL(sps_connect); + +/** + * Disconnect an SPS connection end point + * + * This function disconnects an SPS connection end point. + * The SPS hardware associated with that end point will be disabled. + * For a connection involving system memory (SPS_DEV_HANDLE_MEM), all + * connection resources are deallocated. For a peripheral-to-peripheral + * connection, the resources associated with the connection will not be + * deallocated until both end points are closed. + * + * The client must call sps_connect() for the handle before calling + * this function. + * + * @h - client context for SPS connection end point + * + * @return 0 on success, negative value on error + * + */ +int sps_disconnect(struct sps_pipe *h) +{ + struct sps_pipe *pipe = h; + struct sps_pipe *check; + struct sps_bam *bam; + int result; + + if (pipe == NULL) { + SPS_ERR(sps, "sps: Invalid pipe\n"); + return SPS_ERROR; + } + + bam = pipe->bam; + if (bam == NULL) { + SPS_ERR(sps, + "sps: BAM device of this pipe is NULL\n"); + return SPS_ERROR; + } + + SPS_DBG2(bam, + "sps: bam %pa src 0x%pK dest 0x%pK mode %s\n", + BAM_ID(bam), + (void *)pipe->connect.source, + (void *)pipe->connect.destination, + pipe->connect.mode == SPS_MODE_SRC ? "SRC" : "DEST"); + + result = SPS_ERROR; + /* Cross-check client with map table */ + if (pipe->connect.mode == SPS_MODE_SRC) + check = pipe->map->client_src; + else + check = pipe->map->client_dest; + + if (check != pipe) { + SPS_ERR(sps, "sps: Client context is corrupt\n"); + goto exit_err; + } + + /* Disconnect the BAM pipe */ + mutex_lock(&bam->lock); + result = sps_rm_state_change(pipe, SPS_STATE_DISCONNECT); + mutex_unlock(&bam->lock); + if (result) + goto exit_err; + + sps_rm_config_init(&pipe->connect); + result = 0; + +exit_err: + + return result; +} +EXPORT_SYMBOL(sps_disconnect); + +/** + * Register an event object for an SPS connection end point + * + */ +int sps_register_event(struct sps_pipe *h, struct sps_register_event *reg) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (reg == NULL) { + SPS_ERR(sps, "sps: registered event is NULL\n"); + return SPS_ERROR; + } + + if (sps == NULL) + return -ENODEV; + + if (!sps->is_ready) { + SPS_ERR(sps, "sps: sps driver not ready\n"); + return -EAGAIN; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + SPS_DBG2(bam, "sps: events:%d\n", reg->options); + + result = sps_bam_pipe_reg_event(bam, pipe->pipe_index, reg); + sps_bam_unlock(bam); + if (result) + SPS_ERR(bam, + "sps:Fail to register event for BAM %pa pipe %d\n", + &pipe->bam->props.phys_addr, pipe->pipe_index); + + return result; +} +EXPORT_SYMBOL(sps_register_event); + +/** + * Enable an SPS connection end point + * + */ +int sps_flow_on(struct sps_pipe *h) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result = 0; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + bam_pipe_halt(&bam->base, pipe->pipe_index, false); + + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_flow_on); + +/** + * Disable an SPS connection end point + * + */ +int sps_flow_off(struct sps_pipe *h, enum sps_flow_off mode) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result = 0; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + bam_pipe_halt(&bam->base, pipe->pipe_index, true); + + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_flow_off); + +/** + * Check if the flags on a descriptor/iovec are valid + * + * @flags - flags on a descriptor/iovec + * + * @return 0 on success, negative value on error + * + */ +static int sps_check_iovec_flags(u32 flags) +{ + if ((flags & SPS_IOVEC_FLAG_NWD) && + !(flags & (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_CMD))) { + SPS_ERR(sps, + "sps: NWD is only valid with EOT or CMD\n"); + return SPS_ERROR; + } else if ((flags & SPS_IOVEC_FLAG_EOT) && + (flags & SPS_IOVEC_FLAG_CMD)) { + SPS_ERR(sps, + "sps: EOT and CMD are not allowed to coexist\n"); + return SPS_ERROR; + } else if (!(flags & SPS_IOVEC_FLAG_CMD) && + (flags & (SPS_IOVEC_FLAG_LOCK | SPS_IOVEC_FLAG_UNLOCK))) { + SPS_ERR(sps, + "sps: pipe lock/unlock flags are only valid with Command Descriptor\n"); + return SPS_ERROR; + } else if ((flags & SPS_IOVEC_FLAG_LOCK) && + (flags & SPS_IOVEC_FLAG_UNLOCK)) { + SPS_ERR(sps, + "sps: Can't lock and unlock a pipe by the same Command Descriptor\n"); + return SPS_ERROR; + } else if ((flags & SPS_IOVEC_FLAG_IMME) && + (flags & SPS_IOVEC_FLAG_CMD)) { + SPS_ERR(sps, + "sps: Immediate and CMD are not allowed to coexist\n"); + return SPS_ERROR; + } else if ((flags & SPS_IOVEC_FLAG_IMME) && + (flags & SPS_IOVEC_FLAG_NWD)) { + SPS_ERR(sps, + "sps: Immediate and NWD are not allowed to coexist\n"); + return SPS_ERROR; + } + + return 0; +} + +/** + * Perform a DMA transfer on an SPS connection end point + * + */ +int sps_transfer(struct sps_pipe *h, struct sps_transfer *transfer) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + struct sps_iovec *iovec; + int i; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (transfer == NULL) { + SPS_ERR(sps, "sps: transfer is NULL\n"); + return SPS_ERROR; + } else if (transfer->iovec == NULL) { + SPS_ERR(sps, "sps: iovec list is NULL\n"); + return SPS_ERROR; + } else if (transfer->iovec_count == 0) { + SPS_ERR(sps, "sps: iovec list is empty\n"); + return SPS_ERROR; + } else if (transfer->iovec_phys == 0) { + SPS_ERR(sps, + "sps: iovec list address is invalid\n"); + return SPS_ERROR; + } + + /* Verify content of IOVECs */ + iovec = transfer->iovec; + for (i = 0; i < transfer->iovec_count; i++) { + u32 flags = iovec->flags; + + if (iovec->size > SPS_IOVEC_MAX_SIZE) { + SPS_ERR(sps, + "sps: iovec size is invalid\n"); + return SPS_ERROR; + } + + if (sps_check_iovec_flags(flags)) + return SPS_ERROR; + + iovec++; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + result = sps_bam_pipe_transfer(bam, pipe->pipe_index, transfer); + + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_transfer); + +/** + * Perform a single DMA transfer on an SPS connection end point + * + */ +int sps_transfer_one(struct sps_pipe *h, phys_addr_t addr, u32 size, + void *user, u32 flags) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } + + if (sps_check_iovec_flags(flags)) + return SPS_ERROR; + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + result = sps_bam_pipe_transfer_one(bam, pipe->pipe_index, + SPS_GET_LOWER_ADDR(addr), size, user, + DESC_FLAG_WORD(flags, addr)); + + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_transfer_one); + +/** + * Read event queue for an SPS connection end point + * + */ +int sps_get_event(struct sps_pipe *h, struct sps_event_notify *notify) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (notify == NULL) { + SPS_ERR(sps, "sps: event_notify is NULL\n"); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + result = sps_bam_pipe_get_event(bam, pipe->pipe_index, notify); + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_get_event); + +/** + * Determine whether an SPS connection end point FIFO is empty + * + */ +int sps_is_pipe_empty(struct sps_pipe *h, u32 *empty) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (empty == NULL) { + SPS_ERR(sps, "sps: result pointer is NULL\n"); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + result = sps_bam_pipe_is_empty(bam, pipe->pipe_index, empty); + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_is_pipe_empty); + +/** + * Get number of free transfer entries for an SPS connection end point + * + */ +int sps_get_free_count(struct sps_pipe *h, u32 *count) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (count == NULL) { + SPS_ERR(sps, "sps: result pointer is NULL\n"); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + result = sps_bam_get_free_count(bam, pipe->pipe_index, count); + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_get_free_count); + +/** + * Reset an SPS BAM device + * + */ +int sps_device_reset(unsigned long dev) +{ + struct sps_bam *bam; + int result; + + if (dev == 0) { + SPS_ERR(sps, + "sps: device handle should not be 0\n"); + return SPS_ERROR; + } + + if (sps == NULL || !sps->is_ready) { + SPS_DBG3(sps, "sps: sps driver is not ready\n"); + return -EPROBE_DEFER; + } + + mutex_lock(&sps->lock); + /* Search for the target BAM device */ + bam = sps_h2bam(dev); + if (bam == NULL) { + SPS_ERR(sps, "sps:Invalid BAM device handle: 0x%pK\n", + (void *)dev); + result = SPS_ERROR; + goto exit_err; + } + + mutex_lock(&bam->lock); + result = sps_bam_reset(bam); + mutex_unlock(&bam->lock); + if (result) { + SPS_ERR(sps, "sps:Fail to reset BAM device: 0x%pK\n", + (void *)dev); + goto exit_err; + } + +exit_err: + mutex_unlock(&sps->lock); + + return result; +} +EXPORT_SYMBOL(sps_device_reset); + +/** + * Get the configuration parameters for an SPS connection end point + * + */ +int sps_get_config(struct sps_pipe *h, struct sps_connect *config) +{ + struct sps_pipe *pipe = h; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (config == NULL) { + SPS_ERR(sps, "sps: config pointer is NULL\n"); + return SPS_ERROR; + } + + if (pipe->bam == NULL) + SPS_DBG(sps, "sps:%s\n", __func__); + else + SPS_DBG(pipe->bam, + "sps: BAM: %pa; pipe index:%d; options:0x%x\n", + BAM_ID(pipe->bam), pipe->pipe_index, + pipe->connect.options); + + /* Copy current client connection state */ + *config = pipe->connect; + + return 0; +} +EXPORT_SYMBOL(sps_get_config); + +/** + * Set the configuration parameters for an SPS connection end point + * + */ +int sps_set_config(struct sps_pipe *h, struct sps_connect *config) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (config == NULL) { + SPS_ERR(sps, "sps: config pointer is NULL\n"); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) { + SPS_ERR(sps, "sps: BAM is NULL\n"); + return SPS_ERROR; + } + + SPS_DBG(bam, "sps: BAM: %pa; pipe index:%d, config-options:0x%x\n", + BAM_ID(bam), pipe->pipe_index, config->options); + + result = sps_bam_pipe_set_params(bam, pipe->pipe_index, + config->options); + if (result == 0) + pipe->connect.options = config->options; + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_set_config); + +/** + * Set ownership of an SPS connection end point + * + */ +int sps_set_owner(struct sps_pipe *h, enum sps_owner owner, + struct sps_satellite *connect) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (connect == NULL) { + SPS_ERR(sps, "sps: connection is NULL\n"); + return SPS_ERROR; + } + + if (owner != SPS_OWNER_REMOTE) { + SPS_ERR(sps, "sps: Unsupported ownership state: %d\n", owner); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + SPS_DBG(bam, "sps: BAM: %pa; pipe index:%d\n", + BAM_ID(bam), pipe->pipe_index); + + result = sps_bam_set_satellite(bam, pipe->pipe_index); + if (result) + goto exit_err; + + /* Return satellite connect info */ + if (connect == NULL) + goto exit_err; + + if (pipe->connect.mode == SPS_MODE_SRC) { + connect->dev = pipe->map->src.bam_phys; + connect->pipe_index = pipe->map->src.pipe_index; + } else { + connect->dev = pipe->map->dest.bam_phys; + connect->pipe_index = pipe->map->dest.pipe_index; + } + connect->config = SPS_CONFIG_SATELLITE; + connect->options = (enum sps_option) 0; + +exit_err: + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_set_owner); + +/** + * Allocate memory from the SPS Pipe-Memory. + * + */ +int sps_alloc_mem(struct sps_pipe *h, enum sps_mem mem, + struct sps_mem_buffer *mem_buffer) +{ + if (sps == NULL) + return -ENODEV; + + if (!sps->is_ready) { + SPS_ERR(sps, "sps: sps driver is not ready\n"); + return -EAGAIN; + } + + if (mem_buffer == NULL || mem_buffer->size == 0) { + SPS_ERR(sps, "sps: invalid memory buffer address or size\n"); + return SPS_ERROR; + } + + if (h == NULL) + SPS_DBG2(sps, + "sps: allocate pipe memory before setup pipe\n"); + else + SPS_DBG2(sps, + "sps:allocate pipe memory for pipe %d\n", + h->pipe_index); + + mem_buffer->phys_base = sps_mem_alloc_io(mem_buffer->size); + if (mem_buffer->phys_base == SPS_ADDR_INVALID) { + SPS_ERR(sps, "sps: invalid address of allocated memory\n"); + return SPS_ERROR; + } + + mem_buffer->base = spsi_get_mem_ptr(mem_buffer->phys_base); + + return 0; +} +EXPORT_SYMBOL(sps_alloc_mem); + +/** + * Free memory from the SPS Pipe-Memory. + * + */ +int sps_free_mem(struct sps_pipe *h, struct sps_mem_buffer *mem_buffer) +{ + SPS_DBG(sps, "sps: Enter\n"); + + if (mem_buffer == NULL || mem_buffer->phys_base == SPS_ADDR_INVALID) { + SPS_ERR(sps, "sps: invalid memory to free\n"); + return SPS_ERROR; + } + + if (h == NULL) + SPS_DBG2(sps, "sps: free pipe memory\n"); + else + SPS_DBG2(sps, + "sps:free pipe memory for pipe %d\n", h->pipe_index); + + sps_mem_free_io(mem_buffer->phys_base, mem_buffer->size); + + return 0; +} +EXPORT_SYMBOL(sps_free_mem); + +/** + * Get the number of unused descriptors in the descriptor FIFO + * of a pipe + * + */ +int sps_get_unused_desc_num(struct sps_pipe *h, u32 *desc_num) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (desc_num == NULL) { + SPS_ERR(sps, "sps: result pointer is NULL\n"); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) + return SPS_ERROR; + + SPS_DBG(bam, "sps: BAM: %pa; pipe index:%d\n", BAM_ID(bam), + pipe->pipe_index); + + result = sps_bam_pipe_get_unused_desc_num(bam, pipe->pipe_index, + desc_num); + + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_get_unused_desc_num); + +/** + * Vote for or relinquish BAM DMA clock + * + */ +int sps_ctrl_bam_dma_clk(bool clk_on) +{ + int ret; + + if (sps == NULL || !sps->is_ready) { + SPS_DBG3(sps, "sps: sps driver is not ready\n"); + return -EPROBE_DEFER; + } + + if (clk_on) { + SPS_DBG1(sps, "%s", "sps:vote for bam dma clk\n"); + ret = clk_prepare_enable(sps->bamdma_clk); + if (ret) { + SPS_ERR(sps, + "sps:fail to enable bamdma_clk:ret=%d\n", ret); + return ret; + } + } else { + SPS_DBG1(sps, "%s", "sps:relinquish bam dma clk\n"); + clk_disable_unprepare(sps->bamdma_clk); + } + + return 0; +} +EXPORT_SYMBOL(sps_ctrl_bam_dma_clk); + +/** + * Register a BAM device + * + */ +int sps_register_bam_device(const struct sps_bam_props *bam_props, + unsigned long *dev_handle) +{ + struct sps_bam *bam = NULL; + void __iomem *virt_addr = NULL; + char bam_name[MAX_MSG_LEN]; + u32 manage; + int ok; + int result; + + if (bam_props == NULL) { + SPS_ERR(sps, "sps: bam_props is NULL\n"); + return SPS_ERROR; + } else if (dev_handle == NULL) { + SPS_ERR(sps, "sps: device handle is NULL\n"); + return SPS_ERROR; + } + + if (sps == NULL) { + pr_err("sps: sps driver is not ready\n"); + return -EPROBE_DEFER; + } + + SPS_DBG3(sps, "sps: Client requests to register BAM %pa\n", + &bam_props->phys_addr); + + /* BAM-DMA is registered internally during power-up */ + if ((!sps->is_ready) && !(bam_props->options & SPS_BAM_OPT_BAMDMA)) { + SPS_ERR(sps, "sps: sps driver not ready\n"); + return -EAGAIN; + } + + /* Check BAM parameters */ + manage = bam_props->manage & SPS_BAM_MGR_ACCESS_MASK; + if (manage != SPS_BAM_MGR_NONE) { + if (bam_props->virt_addr == NULL && bam_props->virt_size == 0) { + SPS_ERR(sps, "sps:Invalid properties for BAM: %pa\n", + &bam_props->phys_addr); + return SPS_ERROR; + } + } + if ((bam_props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) { + /* BAM global is configured by local processor */ + if (bam_props->summing_threshold == 0) { + SPS_ERR(sps, + "sps:Invalid device ctrl properties for BAM: %pa\n", + &bam_props->phys_addr); + return SPS_ERROR; + } + } + manage = bam_props->manage & + (SPS_BAM_MGR_PIPE_NO_CONFIG | SPS_BAM_MGR_PIPE_NO_CTRL); + + /* In case of error */ + *dev_handle = SPS_DEV_HANDLE_INVALID; + result = SPS_ERROR; + + mutex_lock(&sps->lock); + /* Is this BAM already registered? */ + bam = phy2bam(bam_props->phys_addr); + if (bam != NULL) { + mutex_unlock(&sps->lock); + SPS_ERR(sps, "sps:BAM is already registered: %pa\n", + &bam->props.phys_addr); + result = -EEXIST; + bam = NULL; /* Avoid error clean-up kfree(bam) */ + goto exit_err; + } + + /* Perform virtual mapping if required */ + if ((bam_props->manage & SPS_BAM_MGR_ACCESS_MASK) != + SPS_BAM_MGR_NONE && bam_props->virt_addr == NULL) { + /* Map the memory region */ + virt_addr = ioremap(bam_props->phys_addr, bam_props->virt_size); + if (virt_addr == NULL) { + SPS_ERR(sps, + "sps:Unable to map BAM IO mem:%pa size:0x%x\n", + &bam_props->phys_addr, bam_props->virt_size); + goto exit_err; + } + } + + bam = kzalloc(sizeof(*bam), GFP_KERNEL); + if (bam == NULL) + goto exit_err; + + memset(bam, 0, sizeof(*bam)); + + mutex_init(&bam->lock); + mutex_lock(&bam->lock); + + /* Copy configuration to BAM device descriptor */ + bam->props = *bam_props; + if (virt_addr != NULL) + bam->props.virt_addr = virt_addr; + + snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_0", + &bam->props.phys_addr); + bam->ipc_log0 = ipc_log_context_create(SPS_IPC_LOGPAGES, + bam_name, 0); + if (!bam->ipc_log0) + SPS_ERR(sps, "unable to create IPC Log 0 for bam %pa\n", + &bam->props.phys_addr); + + snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_1", + &bam->props.phys_addr); + bam->ipc_log1 = ipc_log_context_create(SPS_IPC_LOGPAGES, + bam_name, 0); + if (!bam->ipc_log1) + SPS_ERR(sps, "unable to create IPC Log 1 for bam %pa\n", + &bam->props.phys_addr); + + snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_2", + &bam->props.phys_addr); + bam->ipc_log2 = ipc_log_context_create(SPS_IPC_LOGPAGES, + bam_name, 0); + if (!bam->ipc_log2) + SPS_ERR(sps, "unable to create IPC Log 2 for bam %pa\n", + &bam->props.phys_addr); + + snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_3", + &bam->props.phys_addr); + bam->ipc_log3 = ipc_log_context_create(SPS_IPC_LOGPAGES, + bam_name, 0); + if (!bam->ipc_log3) + SPS_ERR(sps, "unable to create IPC Log 3 for bam %pa\n", + &bam->props.phys_addr); + + snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_4", + &bam->props.phys_addr); + bam->ipc_log4 = ipc_log_context_create(SPS_IPC_LOGPAGES, + bam_name, 0); + if (!bam->ipc_log4) + SPS_ERR(sps, "unable to create IPC Log 4 for bam %pa\n", + &bam->props.phys_addr); + + if (bam_props->ipc_loglevel) + bam->ipc_loglevel = bam_props->ipc_loglevel; + else + bam->ipc_loglevel = SPS_IPC_DEFAULT_LOGLEVEL; + + ok = sps_bam_device_init(bam); + mutex_unlock(&bam->lock); + if (ok) { + SPS_ERR(bam, "sps:Fail to init BAM device: phys %pa\n", + &bam->props.phys_addr); + goto exit_err; + } + + /* Add BAM to the list */ + list_add_tail(&bam->list, &sps->bams_q); + *dev_handle = (uintptr_t) bam; + + result = 0; +exit_err: + mutex_unlock(&sps->lock); + + if (result) { + if (bam != NULL) { + if (virt_addr != NULL) + iounmap(bam->props.virt_addr); + kfree(bam); + } + + return result; + } + + /* If this BAM is attached to a BAM-DMA, init the BAM-DMA device */ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) { + if (sps_dma_device_init((uintptr_t) bam)) { + bam->props.options &= ~SPS_BAM_OPT_BAMDMA; + sps_deregister_bam_device((uintptr_t) bam); + SPS_ERR(bam, "sps:Fail to init BAM-DMA BAM: phys %pa\n", + &bam->props.phys_addr); + return SPS_ERROR; + } + } +#endif /* CONFIG_SPS_SUPPORT_BAMDMA */ + + SPS_INFO(bam, "sps:BAM %pa is registered\n", &bam->props.phys_addr); + + return 0; +} +EXPORT_SYMBOL(sps_register_bam_device); + +/** + * Deregister a BAM device + * + */ +int sps_deregister_bam_device(unsigned long dev_handle) +{ + struct sps_bam *bam; + int n; + + if (dev_handle == 0) { + SPS_ERR(sps, "sps: device handle should not be 0\n"); + return SPS_ERROR; + } + + bam = sps_h2bam(dev_handle); + if (bam == NULL) { + SPS_ERR(sps, "sps: did not find a BAM for this handle\n"); + return SPS_ERROR; + } + + SPS_DBG3(sps, "sps: SPS deregister BAM: phys %pa\n", + &bam->props.phys_addr); + + if (bam->props.options & SPS_BAM_HOLD_MEM) { + for (n = 0; n < BAM_MAX_PIPES; n++) + kfree(bam->desc_cache_pointers[n]); + } + + /* If this BAM is attached to a BAM-DMA, init the BAM-DMA device */ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) { + mutex_lock(&bam->lock); + (void)sps_dma_device_de_init((uintptr_t) bam); + bam->props.options &= ~SPS_BAM_OPT_BAMDMA; + mutex_unlock(&bam->lock); + } +#endif + + /* Remove the BAM from the registration list */ + mutex_lock(&sps->lock); + list_del(&bam->list); + mutex_unlock(&sps->lock); + + /* De-init the BAM and free resources */ + mutex_lock(&bam->lock); + sps_bam_device_de_init(bam); + mutex_unlock(&bam->lock); + ipc_log_context_destroy(bam->ipc_log0); + ipc_log_context_destroy(bam->ipc_log1); + ipc_log_context_destroy(bam->ipc_log2); + ipc_log_context_destroy(bam->ipc_log3); + ipc_log_context_destroy(bam->ipc_log4); + if (bam->props.virt_size) + (void)iounmap(bam->props.virt_addr); + + kfree(bam); + + return 0; +} +EXPORT_SYMBOL(sps_deregister_bam_device); + +/** + * Get processed I/O vector (completed transfers) + * + */ +int sps_get_iovec(struct sps_pipe *h, struct sps_iovec *iovec) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (iovec == NULL) { + SPS_ERR(sps, "sps: iovec pointer is NULL\n"); + return SPS_ERROR; + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) { + SPS_ERR(sps, "sps: BAM is not found by handle\n"); + return SPS_ERROR; + } + + SPS_DBG(bam, "sps: BAM: %pa; pipe index:%d\n", + BAM_ID(bam), pipe->pipe_index); + + /* Get an iovec from the BAM pipe descriptor FIFO */ + result = sps_bam_pipe_get_iovec(bam, pipe->pipe_index, iovec); + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_get_iovec); + +/** + * Perform timer control + * + */ +int sps_timer_ctrl(struct sps_pipe *h, + struct sps_timer_ctrl *timer_ctrl, + struct sps_timer_result *timer_result) +{ + struct sps_pipe *pipe = h; + struct sps_bam *bam; + int result; + + if (h == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } else if (timer_ctrl == NULL) { + SPS_ERR(sps, "sps: timer_ctrl pointer is NULL\n"); + return SPS_ERROR; + } else if (timer_result == NULL) { + SPS_DBG(sps, "sps: no result to return\n"); + } + + bam = sps_bam_lock(pipe); + if (bam == NULL) { + SPS_ERR(sps, "sps: BAM is not found by handle\n"); + return SPS_ERROR; + } + + SPS_DBG2(bam, "sps: BAM: %pa; pipe index:%d\n", + BAM_ID(bam), pipe->pipe_index); + + /* Perform the BAM pipe timer control operation */ + result = sps_bam_pipe_timer_ctrl(bam, pipe->pipe_index, timer_ctrl, + timer_result); + sps_bam_unlock(bam); + + return result; +} +EXPORT_SYMBOL(sps_timer_ctrl); + +/* + * Reset a BAM pipe + */ +int sps_pipe_reset(unsigned long dev, u32 pipe) +{ + struct sps_bam *bam; + + if (!dev) { + SPS_ERR(sps, "sps: BAM handle is NULL\n"); + return SPS_ERROR; + } + + if (pipe >= BAM_MAX_PIPES) { + SPS_ERR(sps, "sps: pipe index is invalid\n"); + return SPS_ERROR; + } + + bam = sps_h2bam(dev); + if (bam == NULL) { + SPS_ERR(sps, "sps: BAM is not found by handle\n"); + return SPS_ERROR; + } + + SPS_DBG2(bam, "sps: BAM: %pa; pipe index:%d\n", BAM_ID(bam), pipe); + + bam_pipe_reset(&bam->base, pipe); + + return 0; +} +EXPORT_SYMBOL(sps_pipe_reset); + +/* + * Disable a BAM pipe + */ +int sps_pipe_disable(unsigned long dev, u32 pipe) +{ + struct sps_bam *bam; + + if (!dev) { + SPS_ERR(sps, "sps: BAM handle is NULL\n"); + return SPS_ERROR; + } + + if (pipe >= BAM_MAX_PIPES) { + SPS_ERR(sps, "sps: pipe index is invalid\n"); + return SPS_ERROR; + } + + bam = sps_h2bam(dev); + if (bam == NULL) { + SPS_ERR(sps, "sps: BAM is not found by handle\n"); + return SPS_ERROR; + } + + SPS_DBG(bam, "sps: BAM: %pa; pipe index:%d\n", BAM_ID(bam), pipe); + + bam_disable_pipe(&bam->base, pipe); + + return 0; +} +EXPORT_SYMBOL(sps_pipe_disable); + +/* + * Check pending descriptors in the descriptor FIFO + * of a pipe + */ +int sps_pipe_pending_desc(unsigned long dev, u32 pipe, bool *pending) +{ + + struct sps_bam *bam; + + if (!dev) { + SPS_ERR(sps, "sps: BAM handle is NULL\n"); + return SPS_ERROR; + } + + if (pipe >= BAM_MAX_PIPES) { + SPS_ERR(sps, "sps: pipe index is invalid\n"); + return SPS_ERROR; + } + + if (!pending) { + SPS_ERR(sps, "sps: input flag is NULL\n"); + return SPS_ERROR; + } + + bam = sps_h2bam(dev); + if (bam == NULL) { + SPS_ERR(sps, "sps: :BAM is not found by handle\n"); + return SPS_ERROR; + } + + SPS_DBG(bam, "sps: BAM: %pa; pipe index:%d\n", BAM_ID(bam), pipe); + + *pending = sps_bam_pipe_pending_desc(bam, pipe); + + return 0; +} +EXPORT_SYMBOL(sps_pipe_pending_desc); + +/* + * Process any pending IRQ of a BAM + */ +int sps_bam_process_irq(unsigned long dev) +{ + struct sps_bam *bam; + int ret = 0; + + if (!dev) { + SPS_ERR(sps, "sps: BAM handle is NULL\n"); + return SPS_ERROR; + } + + bam = sps_h2bam(dev); + if (bam == NULL) { + SPS_ERR(sps, "sps: BAM is not found by handle\n"); + return SPS_ERROR; + } + + SPS_DBG1(bam, "sps: BAM: %pa\n", BAM_ID(bam)); + + ret = sps_bam_check_irq(bam); + + return ret; +} +EXPORT_SYMBOL(sps_bam_process_irq); + +/* + * Get address info of a BAM + */ +int sps_get_bam_addr(unsigned long dev, phys_addr_t *base, + u32 *size) +{ + struct sps_bam *bam; + + if (!dev) { + SPS_ERR(sps, "sps: BAM handle is NULL\n"); + return SPS_ERROR; + } + + bam = sps_h2bam(dev); + if (bam == NULL) { + SPS_ERR(sps, "sps: BAM is not found by handle\n"); + return SPS_ERROR; + } + + *base = bam->props.phys_addr; + *size = bam->props.virt_size; + + SPS_DBG2(bam, "sps: BAM: %pa; base:%pa; size:%d\n", + BAM_ID(bam), base, *size); + + return 0; +} +EXPORT_SYMBOL(sps_get_bam_addr); + +/* + * Inject a ZLT with EOT for a BAM pipe + */ +int sps_pipe_inject_zlt(unsigned long dev, u32 pipe_index) +{ + struct sps_bam *bam; + int rc; + + if (!dev) { + SPS_ERR(sps, "sps: BAM handle is NULL\n"); + return SPS_ERROR; + } + + if (pipe_index >= BAM_MAX_PIPES) { + SPS_ERR(sps, "sps: pipe index is invalid\n"); + return SPS_ERROR; + } + + bam = sps_h2bam(dev); + if (bam == NULL) { + SPS_ERR(sps, "sps: BAM is not found by handle\n"); + return SPS_ERROR; + } + + SPS_DBG(bam, "sps: BAM: %pa; pipe index:%d\n", BAM_ID(bam), pipe_index); + + rc = sps_bam_pipe_inject_zlt(bam, pipe_index); + if (rc) + SPS_ERR(bam, "sps: failed to inject a ZLT\n"); + + return rc; +} +EXPORT_SYMBOL(sps_pipe_inject_zlt); + +/** + * Allocate client state context + * + */ +struct sps_pipe *sps_alloc_endpoint(void) +{ + struct sps_pipe *ctx = NULL; + + SPS_DBG(sps, "sps: Enter\n"); + + ctx = kzalloc(sizeof(struct sps_pipe), GFP_KERNEL); + if (ctx == NULL) + return NULL; + + sps_client_init(ctx); + + return ctx; +} +EXPORT_SYMBOL(sps_alloc_endpoint); + +/** + * Free client state context + * + */ +int sps_free_endpoint(struct sps_pipe *ctx) +{ + int res; + + SPS_DBG(sps, "sps: Enter\n"); + + if (ctx == NULL) { + SPS_ERR(sps, "sps: pipe is NULL\n"); + return SPS_ERROR; + } + + res = sps_client_de_init(ctx); + + if (res == 0) + kfree(ctx); + + return res; +} +EXPORT_SYMBOL(sps_free_endpoint); + +/** + * Platform Driver. + */ +static int get_platform_data(struct platform_device *pdev) +{ + struct resource *resource; + struct msm_sps_platform_data *pdata; + + SPS_DBG3(sps, "sps: Enter\n"); + + pdata = pdev->dev.platform_data; + + if (pdata == NULL) { + SPS_ERR(sps, "sps: invalid platform data\n"); + sps->bamdma_restricted_pipes = 0; + return -EINVAL; + } + sps->bamdma_restricted_pipes = pdata->bamdma_restricted_pipes; + SPS_DBG3(sps, "sps:bamdma_restricted_pipes=0x%x\n", + sps->bamdma_restricted_pipes); + + resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pipe_mem"); + if (resource) { + sps->pipemem_phys_base = resource->start; + sps->pipemem_size = resource_size(resource); + SPS_DBG3(sps, "sps:pipemem.base=%pa,size=0x%x\n", + &sps->pipemem_phys_base, + sps->pipemem_size); + } + +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "bamdma_bam"); + if (resource) { + sps->bamdma_bam_phys_base = resource->start; + sps->bamdma_bam_size = resource_size(resource); + SPS_DBG(sps, "sps:bamdma_bam.base=%pa,size=0x%x\n", + &sps->bamdma_bam_phys_base, + sps->bamdma_bam_size); + } + + resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "bamdma_dma"); + if (resource) { + sps->bamdma_dma_phys_base = resource->start; + sps->bamdma_dma_size = resource_size(resource); + SPS_DBG(sps, "sps:bamdma_dma.base=%pa,size=0x%x\n", + &sps->bamdma_dma_phys_base, + sps->bamdma_dma_size); + } + + resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "bamdma_irq"); + if (resource) { + sps->bamdma_irq = resource->start; + SPS_DBG(sps, "sps:bamdma_irq=%d\n", sps->bamdma_irq); + } +#endif + + return 0; +} + +/** + * Read data from device tree + */ +static int get_device_tree_data(struct platform_device *pdev) +{ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + struct resource *resource; + + SPS_DBG(sps, "sps: Enter\n"); + + if (of_property_read_u32((&pdev->dev)->of_node, + "qcom,bam-dma-res-pipes", + &sps->bamdma_restricted_pipes)) + SPS_DBG(sps, + "sps: No restricted bamdma pipes on this target\n"); + else + SPS_DBG(sps, "sps:bamdma_restricted_pipes=0x%x\n", + sps->bamdma_restricted_pipes); + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (resource) { + sps->bamdma_bam_phys_base = resource->start; + sps->bamdma_bam_size = resource_size(resource); + SPS_DBG(sps, "sps:bamdma_bam.base=%pa,size=0x%x\n", + &sps->bamdma_bam_phys_base, + sps->bamdma_bam_size); + } else { + SPS_ERR(sps, "sps: BAM DMA BAM mem unavailable\n"); + return -ENODEV; + } + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (resource) { + sps->bamdma_dma_phys_base = resource->start; + sps->bamdma_dma_size = resource_size(resource); + SPS_DBG(sps, "sps:bamdma_dma.base=%pa,size=0x%x\n", + &sps->bamdma_dma_phys_base, + sps->bamdma_dma_size); + } else { + SPS_ERR(sps, "sps: BAM DMA mem unavailable\n"); + return -ENODEV; + } + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (resource) { + imem = true; + sps->pipemem_phys_base = resource->start; + sps->pipemem_size = resource_size(resource); + SPS_DBG(sps, "sps:pipemem.base=%pa,size=0x%x\n", + &sps->pipemem_phys_base, + sps->pipemem_size); + } else { + imem = false; + SPS_DBG(sps, "sps: No pipe memory on this target\n"); + } + + resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (resource) { + sps->bamdma_irq = resource->start; + SPS_DBG(sps, "sps:bamdma_irq=%d\n", sps->bamdma_irq); + } else { + SPS_ERR(sps, "sps: BAM DMA IRQ unavailable\n"); + return -ENODEV; + } +#endif + + if (of_property_read_u32((&pdev->dev)->of_node, + "qcom,device-type", + &d_type)) { + d_type = 3; + SPS_DBG3(sps, "sps:default device type %d\n", d_type); + } else + SPS_DBG3(sps, "sps:device type is %d\n", d_type); + + enhd_pipe = of_property_read_bool((&pdev->dev)->of_node, + "qcom,pipe-attr-ee"); + SPS_DBG3(sps, "sps:PIPE_ATTR_EE is %s supported\n", + (enhd_pipe ? "" : "not")); + + return 0; +} + +static const struct of_device_id msm_sps_match[] = { + { .compatible = "qcom,msm-sps", + .data = &bam_types[SPS_BAM_NDP] + }, + { .compatible = "qcom,msm-sps-4k", + .data = &bam_types[SPS_BAM_NDP_4K] + }, + {}, +}; + +static int msm_sps_probe(struct platform_device *pdev) +{ + int ret = -ENODEV; + + SPS_DBG3(sps, "sps: Enter\n"); + + if (pdev->dev.of_node) { + const struct of_device_id *match; + + if (get_device_tree_data(pdev)) { + SPS_ERR(sps, + "sps: Fail to get data from device tree\n"); + return -ENODEV; + } + SPS_DBG(sps, "%s", "sps:get data from device tree\n"); + + match = of_match_device(msm_sps_match, &pdev->dev); + if (match) { + bam_type = *((enum sps_bam_type *)(match->data)); + SPS_DBG3(sps, "sps:BAM type is:%d\n", bam_type); + } else { + bam_type = SPS_BAM_NDP; + SPS_DBG3(sps, "sps:use default BAM type:%d\n", + bam_type); + } + } else { + d_type = 0; + if (get_platform_data(pdev)) { + SPS_ERR(sps, "sps: :Fail to get platform data\n"); + return -ENODEV; + } + SPS_DBG(sps, "%s", "sps:get platform data\n"); + bam_type = SPS_BAM_LEGACY; + } + + /* Create Device */ + sps->dev_class = class_create(THIS_MODULE, SPS_DRV_NAME); + + ret = alloc_chrdev_region(&sps->dev_num, 0, 1, SPS_DRV_NAME); + if (ret) { + SPS_ERR(sps, "sps: alloc_chrdev_region err\n"); + goto alloc_chrdev_region_err; + } + + sps->dev = device_create(sps->dev_class, NULL, sps->dev_num, sps, + SPS_DRV_NAME); + if (IS_ERR(sps->dev)) { + SPS_ERR(sps, "sps: device_create err\n"); + goto device_create_err; + } + + if (pdev->dev.of_node) + sps->dev->of_node = pdev->dev.of_node; + + if (!d_type) { + sps->pmem_clk = clk_get(sps->dev, "mem_clk"); + if (IS_ERR(sps->pmem_clk)) { + if (PTR_ERR(sps->pmem_clk) == -EPROBE_DEFER) + ret = -EPROBE_DEFER; + else + SPS_ERR(sps, "sps: fail to get pmem_clk\n"); + goto pmem_clk_err; + } else { + ret = clk_prepare_enable(sps->pmem_clk); + if (ret) { + SPS_ERR(sps, + "sps: failed to enable pmem_clk\n"); + goto pmem_clk_en_err; + } + } + } + +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + sps->dfab_clk = clk_get(sps->dev, "dfab_clk"); + if (IS_ERR(sps->dfab_clk)) { + if (PTR_ERR(sps->dfab_clk) == -EPROBE_DEFER) + ret = -EPROBE_DEFER; + else + SPS_ERR(sps, "sps: fail to get dfab_clk\n"); + goto dfab_clk_err; + } else { + ret = clk_set_rate(sps->dfab_clk, 64000000); + if (ret) { + SPS_ERR(sps, "sps: failed to set dfab_clk rate\n"); + clk_put(sps->dfab_clk); + goto dfab_clk_err; + } + } + + sps->bamdma_clk = clk_get(sps->dev, "dma_bam_pclk"); + if (IS_ERR(sps->bamdma_clk)) { + if (PTR_ERR(sps->bamdma_clk) == -EPROBE_DEFER) + ret = -EPROBE_DEFER; + else + SPS_ERR(sps, "sps: fail to get bamdma_clk\n"); + clk_put(sps->dfab_clk); + goto dfab_clk_err; + } else { + ret = clk_prepare_enable(sps->bamdma_clk); + if (ret) { + SPS_ERR(sps, "sps:failed to enable bamdma_clk ret=%d\n", + ret); + clk_put(sps->bamdma_clk); + clk_put(sps->dfab_clk); + goto dfab_clk_err; + } + } + + ret = clk_prepare_enable(sps->dfab_clk); + if (ret) { + SPS_ERR(sps, "sps:failed to enable dfab_clk ret=%d\n", ret); + clk_disable_unprepare(sps->bamdma_clk); + clk_put(sps->bamdma_clk); + clk_put(sps->dfab_clk); + goto dfab_clk_err; + } +#endif + ret = sps_device_init(); + if (ret) { + SPS_ERR(sps, "sps: sps_device_init err\n"); + +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + clk_disable_unprepare(sps->dfab_clk); + clk_disable_unprepare(sps->bamdma_clk); + clk_put(sps->bamdma_clk); + clk_put(sps->dfab_clk); +#endif + goto dfab_clk_err; + } +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + clk_disable_unprepare(sps->dfab_clk); + clk_disable_unprepare(sps->bamdma_clk); +#endif + sps->is_ready = true; + + SPS_INFO(sps, "%s", "sps:sps is ready\n"); + + return 0; +dfab_clk_err: + if (!d_type) + clk_disable_unprepare(sps->pmem_clk); +pmem_clk_en_err: + if (!d_type) + clk_put(sps->pmem_clk); +pmem_clk_err: + device_destroy(sps->dev_class, sps->dev_num); +device_create_err: + unregister_chrdev_region(sps->dev_num, 1); +alloc_chrdev_region_err: + class_destroy(sps->dev_class); + + return ret; +} + +static int msm_sps_remove(struct platform_device *pdev) +{ + SPS_DBG3(sps, "sps: Enter\n"); + + device_destroy(sps->dev_class, sps->dev_num); + unregister_chrdev_region(sps->dev_num, 1); + class_destroy(sps->dev_class); + sps_device_de_init(); + + clk_put(sps->dfab_clk); + if (!d_type) + clk_put(sps->pmem_clk); + clk_put(sps->bamdma_clk); + + return 0; +} + +static struct platform_driver msm_sps_driver = { + .probe = msm_sps_probe, + .driver = { + .name = SPS_DRV_NAME, + .of_match_table = msm_sps_match, + .suppress_bind_attrs = true, + }, + .remove = msm_sps_remove, +}; + +/** + * Module Init. + */ +static int __init sps_init(void) +{ + int ret; + +#ifdef CONFIG_DEBUG_FS + sps_debugfs_init(); +#endif + + pr_debug("sps:%s\n", __func__); + + /* Allocate the SPS driver state struct */ + sps = kzalloc(sizeof(*sps), GFP_KERNEL); + if (sps == NULL) + return -ENOMEM; + + sps->ipc_log0 = ipc_log_context_create(SPS_IPC_LOGPAGES, + "sps_ipc_log0", 0); + if (!sps->ipc_log0) + pr_err("Failed to create IPC log0\n"); + sps->ipc_log1 = ipc_log_context_create(SPS_IPC_LOGPAGES, + "sps_ipc_log1", 0); + if (!sps->ipc_log1) + pr_err("Failed to create IPC log1\n"); + sps->ipc_log2 = ipc_log_context_create(SPS_IPC_LOGPAGES, + "sps_ipc_log2", 0); + if (!sps->ipc_log2) + pr_err("Failed to create IPC log2\n"); + sps->ipc_log3 = ipc_log_context_create(SPS_IPC_LOGPAGES, + "sps_ipc_log3", 0); + if (!sps->ipc_log3) + pr_err("Failed to create IPC log3\n"); + sps->ipc_log4 = ipc_log_context_create(SPS_IPC_LOGPAGES * + SPS_IPC_REG_DUMP_FACTOR, "sps_ipc_log4", 0); + if (!sps->ipc_log4) + pr_err("Failed to create IPC log4\n"); + + ret = platform_driver_register(&msm_sps_driver); + + return ret; +} + +/** + * Module Exit. + */ +static void __exit sps_exit(void) +{ + pr_debug("sps:%s\n", __func__); + + platform_driver_unregister(&msm_sps_driver); + + kfree(sps); + sps = NULL; + +#ifdef CONFIG_DEBUG_FS + sps_debugfs_exit(); +#endif +} + +arch_initcall(sps_init); +module_exit(sps_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Smart Peripheral Switch (SPS)"); diff --git a/drivers/soc/qcom/sps/sps_bam.c b/drivers/soc/qcom/sps/sps_bam.c new file mode 100644 index 000000000000..87d0b40278e9 --- /dev/null +++ b/drivers/soc/qcom/sps/sps_bam.c @@ -0,0 +1,2503 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sps_bam.h" +#include "bam.h" +#include "spsi.h" + +/* All BAM global IRQ sources */ +#define BAM_IRQ_ALL (BAM_DEV_IRQ_HRESP_ERROR | BAM_DEV_IRQ_ERROR | \ + BAM_DEV_IRQ_TIMER) + +/* BAM device state flags */ +#define BAM_STATE_INIT (1UL << 1) +#define BAM_STATE_IRQ (1UL << 2) +#define BAM_STATE_ENABLED (1UL << 3) +#define BAM_STATE_BAM2BAM (1UL << 4) +#define BAM_STATE_MTI (1UL << 5) +#define BAM_STATE_REMOTE (1UL << 6) + +/* Mask for valid hardware descriptor flags */ +#define BAM_IOVEC_FLAG_MASK \ + (SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_EOB | \ + SPS_IOVEC_FLAG_NWD | SPS_IOVEC_FLAG_CMD | SPS_IOVEC_FLAG_LOCK | \ + SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_IMME) + +/* Mask for invalid BAM-to-BAM pipe options */ +#define BAM2BAM_O_INVALID \ + (SPS_O_DESC_DONE | \ + SPS_O_EOT | \ + SPS_O_POLL | \ + SPS_O_NO_Q | \ + SPS_O_ACK_TRANSFERS) + +/** + * Pipe/client pointer value indicating pipe is allocated, but no client has + * been assigned + */ +#define BAM_PIPE_UNASSIGNED ((struct sps_pipe *)((~0x0ul) - 0x88888888)) + +/* Check whether pipe has been assigned */ +#define BAM_PIPE_IS_ASSIGNED(p) \ + (((p) != NULL) && ((p) != BAM_PIPE_UNASSIGNED)) + +/* Is MTI use supported for a specific BAM version? */ +#define BAM_VERSION_MTI_SUPPORT(ver) (ver <= 2) + +/* Event option<->event translation table entry */ +struct sps_bam_opt_event_table { + enum sps_event event_id; + enum sps_option option; + enum bam_pipe_irq pipe_irq; +}; + +static const struct sps_bam_opt_event_table opt_event_table[] = { + {SPS_EVENT_EOT, SPS_O_EOT, BAM_PIPE_IRQ_EOT}, + {SPS_EVENT_DESC_DONE, SPS_O_DESC_DONE, BAM_PIPE_IRQ_DESC_INT}, + {SPS_EVENT_WAKEUP, SPS_O_WAKEUP, BAM_PIPE_IRQ_WAKE}, + {SPS_EVENT_INACTIVE, SPS_O_INACTIVE, BAM_PIPE_IRQ_TIMER}, + {SPS_EVENT_OUT_OF_DESC, SPS_O_OUT_OF_DESC, + BAM_PIPE_IRQ_OUT_OF_DESC}, + {SPS_EVENT_ERROR, SPS_O_ERROR, BAM_PIPE_IRQ_ERROR}, + {SPS_EVENT_RST_ERROR, SPS_O_RST_ERROR, BAM_PIPE_IRQ_RST_ERROR}, + {SPS_EVENT_HRESP_ERROR, SPS_O_HRESP_ERROR, BAM_PIPE_IRQ_HRESP_ERROR} +}; + +/* Pipe event source handler */ +static void pipe_handler(struct sps_bam *dev, + struct sps_pipe *pipe); + +/** + * Pipe transfer event (EOT, DESC_DONE) source handler. + * This function is called by pipe_handler() and other functions to process the + * descriptor FIFO. + */ +static void pipe_handler_eot(struct sps_bam *dev, + struct sps_pipe *pipe); + +/** + * BAM driver initialization + */ +int sps_bam_driver_init(u32 options) +{ + int n; + + /* + * Check that SPS_O_ and BAM_PIPE_IRQ_ values are identical. + * This is required so that the raw pipe IRQ status can be passed + * to the client in the SPS_EVENT_IRQ. + */ + for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) { + if ((u32)opt_event_table[n].option != + (u32)opt_event_table[n].pipe_irq) { + SPS_ERR(sps, "sps:SPS_O 0x%x != HAL IRQ 0x%x\n", + opt_event_table[n].option, + opt_event_table[n].pipe_irq); + return SPS_ERROR; + } + } + + return 0; +} + +/* + * Check BAM interrupt + */ +int sps_bam_check_irq(struct sps_bam *dev) +{ + struct sps_pipe *pipe; + u32 source; + unsigned long flags = 0; + int ret = 0; + + SPS_DBG1(dev, "sps: bam=%pa\n", BAM_ID(dev)); + + spin_lock_irqsave(&dev->isr_lock, flags); + +polling: + /* Get BAM interrupt source(s) */ + if ((dev->state & BAM_STATE_MTI) == 0) { + u32 mask = dev->pipe_active_mask; + enum sps_callback_case cb_case; + + source = bam_check_irq_source(&dev->base, dev->props.ee, + mask, &cb_case); + + SPS_DBG1(dev, "sps:bam=%pa;source=0x%x;mask=0x%x\n", + BAM_ID(dev), source, mask); + + if ((source == 0) && + (dev->props.options & SPS_BAM_RES_CONFIRM)) { + SPS_DBG2(dev, + "sps: BAM %pa has no source (source = 0x%x)\n", + BAM_ID(dev), source); + + spin_unlock_irqrestore(&dev->isr_lock, flags); + return SPS_ERROR; + } + + if ((source & (1UL << 31)) && (dev->props.callback)) { + SPS_DBG1(dev, "sps:bam=%pa;callback for case %d\n", + BAM_ID(dev), cb_case); + dev->props.callback(cb_case, dev->props.user); + } + + /* Mask any non-local source */ + source &= dev->pipe_active_mask; + } else { + /* If MTIs are used, must poll each active pipe */ + source = dev->pipe_active_mask; + + SPS_DBG1(dev, "sps:MTI:bam=%pa;source=0x%x\n", + BAM_ID(dev), source); + } + + /* Process active pipe sources */ + pipe = list_first_entry(&dev->pipes_q, struct sps_pipe, list); + + list_for_each_entry(pipe, &dev->pipes_q, list) { + /* Check this pipe's bit in the source mask */ + if (BAM_PIPE_IS_ASSIGNED(pipe) + && (!pipe->disconnecting) + && (source & pipe->pipe_index_mask)) { + /* This pipe has an interrupt pending */ + pipe_handler(dev, pipe); + source &= ~pipe->pipe_index_mask; + } + if (source == 0) + break; + } + + /* Process any inactive pipe sources */ + if (source) { + SPS_ERR(dev, "sps:IRQ from BAM %pa inactive pipe(s) 0x%x\n", + BAM_ID(dev), source); + dev->irq_from_disabled_pipe++; + } + + if (dev->props.options & SPS_BAM_RES_CONFIRM) { + u32 mask = dev->pipe_active_mask; + enum sps_callback_case cb_case; + + source = bam_check_irq_source(&dev->base, dev->props.ee, + mask, &cb_case); + + SPS_DBG1(dev, + "sps:check if there is any new IRQ coming:bam=%pa;source=0x%x;mask=0x%x\n", + BAM_ID(dev), source, mask); + + if ((source & (1UL << 31)) && (dev->props.callback)) { + SPS_DBG1(dev, "sps:bam=%pa;callback for case %d\n", + BAM_ID(dev), cb_case); + dev->props.callback(cb_case, dev->props.user); + } + + if (source) + goto polling; + } + + spin_unlock_irqrestore(&dev->isr_lock, flags); + + return ret; +} + +/** + * BAM interrupt service routine + * + * This function is the BAM interrupt service routine. + * + * @ctxt - pointer to ISR's registered argument + * + * @return void + */ +static irqreturn_t bam_isr(int irq, void *ctxt) +{ + struct sps_bam *dev = ctxt; + + SPS_DBG1(dev, "sps: bam:%pa; IRQ #:%d\n", BAM_ID(dev), irq); + + if (dev->props.options & SPS_BAM_RES_CONFIRM) { + if (dev->props.callback) { + bool ready = false; + + dev->props.callback(SPS_CALLBACK_BAM_RES_REQ, &ready); + if (ready) { + SPS_DBG1(dev, + "sps: handle IRQ for bam:%pa IRQ #:%d\n", + BAM_ID(dev), irq); + if (sps_bam_check_irq(dev)) + SPS_DBG2(dev, + "sps: callback bam:%pa IRQ #:%d to poll the pipes\n", + BAM_ID(dev), irq); + dev->props.callback(SPS_CALLBACK_BAM_RES_REL, + &ready); + } else { + SPS_DBG1(dev, + "sps: BAM is not ready and thus skip IRQ for bam:%pa IRQ #:%d\n", + BAM_ID(dev), irq); + } + } else { + SPS_ERR(dev, + "sps: Client of BAM %pa requires confirmation but does not register callback\n", + BAM_ID(dev)); + } + } else { + sps_bam_check_irq(dev); + } + + return IRQ_HANDLED; +} + +/** + * BAM device enable + */ +int sps_bam_enable(struct sps_bam *dev) +{ + u32 num_pipes; + u32 irq_mask; + int result = 0; + int rc; + int MTIenabled; + + /* Is this BAM enabled? */ + if ((dev->state & BAM_STATE_ENABLED)) + return 0; /* Yes, so no work to do */ + + /* Is there any access to this BAM? */ + if ((dev->props.manage & SPS_BAM_MGR_ACCESS_MASK) == SPS_BAM_MGR_NONE) { + SPS_ERR(dev, "sps:No local access to BAM %pa\n", BAM_ID(dev)); + return SPS_ERROR; + } + + /* Set interrupt handling */ + if ((dev->props.options & SPS_BAM_OPT_IRQ_DISABLED) != 0 || + dev->props.irq == SPS_IRQ_INVALID) { + /* Disable the BAM interrupt */ + irq_mask = 0; + dev->state &= ~BAM_STATE_IRQ; + } else { + /* Register BAM ISR */ + if (dev->props.irq > 0) { + if (dev->props.options & SPS_BAM_RES_CONFIRM) { + result = request_irq(dev->props.irq, + (irq_handler_t) bam_isr, + IRQF_TRIGGER_RISING, "sps", dev); + SPS_DBG3(dev, + "sps:BAM %pa uses edge for IRQ# %d\n", + BAM_ID(dev), dev->props.irq); + } else { + result = request_irq(dev->props.irq, + (irq_handler_t) bam_isr, + IRQF_TRIGGER_HIGH, "sps", dev); + SPS_DBG3(dev, + "sps:BAM %pa uses level for IRQ# %d\n", + BAM_ID(dev), dev->props.irq); + } + } else { + SPS_DBG3(dev, + "sps:BAM %pa does not have an valid IRQ# %d\n", + BAM_ID(dev), dev->props.irq); + } + + if (result) { + SPS_ERR(dev, "sps:Failed to enable BAM %pa IRQ %d\n", + BAM_ID(dev), dev->props.irq); + return SPS_ERROR; + } + + /* Enable the BAM interrupt */ + irq_mask = BAM_IRQ_ALL; + dev->state |= BAM_STATE_IRQ; + + /* Register BAM IRQ for apps wakeup */ + if (dev->props.options & SPS_BAM_OPT_IRQ_WAKEUP) { + result = enable_irq_wake(dev->props.irq); + + if (result) { + SPS_ERR(dev, + "sps:Fail to enable wakeup irq for BAM %pa IRQ %d\n", + BAM_ID(dev), dev->props.irq); + return SPS_ERROR; + } + SPS_DBG3(dev, + "sps:Enable wakeup irq for BAM %pa IRQ %d\n", + BAM_ID(dev), dev->props.irq); + } + } + + /* Is global BAM control managed by the local processor? */ + num_pipes = 0; + if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) + /* Yes, so initialize the BAM device */ + rc = bam_init(&dev->base, + dev->props.ee, + (u16) dev->props.summing_threshold, + irq_mask, + &dev->version, &num_pipes, + dev->props.options); + else + /* No, so just verify that it is enabled */ + rc = bam_check(&dev->base, &dev->version, + dev->props.ee, &num_pipes); + + if (rc) { + SPS_ERR(dev, "sps:Fail to init BAM %pa IRQ %d\n", + BAM_ID(dev), dev->props.irq); + return SPS_ERROR; + } + + /* Check if this BAM supports MTIs (Message Triggered Interrupts) or + * multiple EEs (Execution Environments). + * MTI and EE support are mutually exclusive. + */ + MTIenabled = BAM_VERSION_MTI_SUPPORT(dev->version); + + if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 && + (dev->props.manage & SPS_BAM_MGR_MULTI_EE) != 0 && + dev->props.ee == 0 && MTIenabled) { + /* + * BAM global is owned by remote processor and local processor + * must use MTI. Thus, force EE index to a non-zero value to + * insure that EE zero globals can't be modified. + */ + SPS_ERR(dev, + "sps: EE for satellite BAM must be set to non-zero\n"); + return SPS_ERROR; + } + + /* + * Enable MTI use (message triggered interrupt) + * if local processor does not control the global BAM config + * and this BAM supports MTIs. + */ + if ((dev->state & BAM_STATE_IRQ) != 0 && + (dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 && + MTIenabled) { + if (dev->props.irq_gen_addr == 0 || + dev->props.irq_gen_addr == SPS_ADDR_INVALID) { + SPS_ERR(dev, + "sps:MTI destination address not specified for BAM %pa\n", + BAM_ID(dev)); + return SPS_ERROR; + } + dev->state |= BAM_STATE_MTI; + } + + if (num_pipes) { + dev->props.num_pipes = num_pipes; + SPS_DBG3(dev, + "sps:BAM %pa number of pipes reported by hw: %d\n", + BAM_ID(dev), dev->props.num_pipes); + } + + /* Check EE index */ + if (!MTIenabled && dev->props.ee >= SPS_BAM_NUM_EES) { + SPS_ERR(dev, "sps:Invalid EE BAM %pa: %d\n", BAM_ID(dev), + dev->props.ee); + return SPS_ERROR; + } + + /* + * Process EE configuration parameters, + * if specified in the properties + */ + if (!MTIenabled && dev->props.sec_config == SPS_BAM_SEC_DO_CONFIG) { + struct sps_bam_sec_config_props *p_sec = + dev->props.p_sec_config_props; + if (p_sec == NULL) { + SPS_ERR(dev, + "sps:EE config table is not specified for BAM %pa\n", + BAM_ID(dev)); + return SPS_ERROR; + } + + /* + * Set restricted pipes based on the pipes assigned to local EE + */ + dev->props.restricted_pipes = + ~p_sec->ees[dev->props.ee].pipe_mask; + + /* + * If local processor manages the BAM, perform the EE + * configuration + */ + if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) { + u32 ee; + u32 pipe_mask; + int n, i; + + /* + * Verify that there are no overlapping pipe + * assignments + */ + for (n = 0; n < SPS_BAM_NUM_EES - 1; n++) { + for (i = n + 1; i < SPS_BAM_NUM_EES; i++) { + if ((p_sec->ees[n].pipe_mask & + p_sec->ees[i].pipe_mask) != 0) { + SPS_ERR(dev, + "sps:Overlapping pipe assignments for BAM %pa: EEs %d and %d\n", + BAM_ID(dev), n, i); + return SPS_ERROR; + } + } + } + + for (ee = 0; ee < SPS_BAM_NUM_EES; ee++) { + /* + * MSbit specifies EE for the global (top-level) + * BAM interrupt + */ + pipe_mask = p_sec->ees[ee].pipe_mask; + if (ee == dev->props.ee) + pipe_mask |= (1UL << 31); + else + pipe_mask &= ~(1UL << 31); + + bam_security_init(&dev->base, ee, + p_sec->ees[ee].vmid, pipe_mask); + } + } + } + + /* + * If local processor manages the BAM and the BAM supports MTIs + * but does not support multiple EEs, set all restricted pipes + * to MTI mode. + */ + if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0 + && MTIenabled) { + u32 pipe_index; + u32 pipe_mask; + + for (pipe_index = 0, pipe_mask = 1; + pipe_index < dev->props.num_pipes; + pipe_index++, pipe_mask <<= 1) { + if ((pipe_mask & dev->props.restricted_pipes) == 0) + continue; /* This is a local pipe */ + + /* + * Enable MTI with destination address of zero + * (and source mask zero). Pipe is in reset, + * so no interrupt will be generated. + */ + bam_pipe_satellite_mti(&dev->base, pipe_index, 0, + dev->props.ee); + } + } + + dev->state |= BAM_STATE_ENABLED; + + if (!dev->props.constrained_logging || + (dev->props.constrained_logging && dev->props.logging_number)) { + if (dev->props.logging_number > 0) + dev->props.logging_number--; + SPS_INFO(dev, + "sps:BAM %pa (va:0x%pK) enabled: ver:0x%x, number of pipes:%d\n", + BAM_ID(dev), dev->base, dev->version, + dev->props.num_pipes); + } else + SPS_DBG3(dev, + "sps:BAM %pa (va:0x%pK) enabled: ver:0x%x, number of pipes:%d\n", + BAM_ID(dev), dev->base, dev->version, + dev->props.num_pipes); + + return 0; +} + +/** + * BAM device disable + * + */ +int sps_bam_disable(struct sps_bam *dev) +{ + if ((dev->state & BAM_STATE_ENABLED) == 0) + return 0; + + /* Is there any access to this BAM? */ + if ((dev->props.manage & SPS_BAM_MGR_ACCESS_MASK) == SPS_BAM_MGR_NONE) { + SPS_ERR(dev, "sps:No local access to BAM %pa\n", BAM_ID(dev)); + return SPS_ERROR; + } + + /* Is this BAM controlled by the local processor? */ + if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE)) { + /* No, so just mark it disabled */ + dev->state &= ~BAM_STATE_ENABLED; + if ((dev->state & BAM_STATE_IRQ) && (dev->props.irq > 0)) { + free_irq(dev->props.irq, dev); + dev->state &= ~BAM_STATE_IRQ; + } + return 0; + } + + /* Disable BAM (interrupts) */ + if ((dev->state & BAM_STATE_IRQ)) { + bam_exit(&dev->base, dev->props.ee); + + /* Deregister BAM ISR */ + if ((dev->state & BAM_STATE_IRQ)) + if (dev->props.irq > 0) + free_irq(dev->props.irq, dev); + dev->state &= ~BAM_STATE_IRQ; + } + + dev->state &= ~BAM_STATE_ENABLED; + + SPS_DBG3(dev, "sps:BAM %pa disabled\n", BAM_ID(dev)); + + return 0; +} + +/** + * BAM device initialization + */ +int sps_bam_device_init(struct sps_bam *dev) +{ + if (dev->props.virt_addr == NULL) { + SPS_ERR(dev, "sps: NULL BAM virtual address\n"); + return SPS_ERROR; + } + dev->base = (void *) dev->props.virt_addr; + + if (dev->props.num_pipes == 0) { + /* Assume max number of pipes until BAM registers can be read */ + dev->props.num_pipes = BAM_MAX_PIPES; + SPS_DBG3(dev, "sps:BAM %pa: assuming max number of pipes: %d\n", + BAM_ID(dev), dev->props.num_pipes); + } + + /* Init BAM state data */ + dev->state = 0; + dev->pipe_active_mask = 0; + dev->pipe_remote_mask = 0; + INIT_LIST_HEAD(&dev->pipes_q); + + spin_lock_init(&dev->isr_lock); + + spin_lock_init(&dev->connection_lock); + + if ((dev->props.options & SPS_BAM_OPT_ENABLE_AT_BOOT)) + if (sps_bam_enable(dev)) { + SPS_ERR(dev, "sps: Fail to enable bam device\n"); + return SPS_ERROR; + } + + SPS_DBG3(dev, "sps:BAM device: phys %pa IRQ %d\n", + BAM_ID(dev), dev->props.irq); + + return 0; +} + +/** + * BAM device de-initialization + * + */ +int sps_bam_device_de_init(struct sps_bam *dev) +{ + int result; + + SPS_DBG3(dev, "sps:BAM device DEINIT: phys %pa IRQ %d\n", + BAM_ID(dev), dev->props.irq); + + result = sps_bam_disable(dev); + + return result; +} + +/** + * BAM device reset + * + */ +int sps_bam_reset(struct sps_bam *dev) +{ + struct sps_pipe *pipe; + u32 pipe_index; + int result; + + SPS_DBG3(dev, "sps:BAM device RESET: phys %pa IRQ %d\n", + BAM_ID(dev), dev->props.irq); + + /* If BAM is enabled, then disable */ + result = 0; + if ((dev->state & BAM_STATE_ENABLED)) { + /* Verify that no pipes are currently allocated */ + for (pipe_index = 0; pipe_index < dev->props.num_pipes; + pipe_index++) { + pipe = dev->pipes[pipe_index]; + if (BAM_PIPE_IS_ASSIGNED(pipe)) { + SPS_ERR(dev, + "sps:BAM device %pa RESET failed: pipe %d in use\n", + BAM_ID(dev), pipe_index); + result = SPS_ERROR; + break; + } + } + + if (result == 0) + result = sps_bam_disable(dev); + } + + /* BAM will be reset as part of the enable process */ + if (result == 0) + result = sps_bam_enable(dev); + + return result; +} + +/** + * Clear the BAM pipe state struct + * + * This function clears the BAM pipe state struct. + * + * @pipe - pointer to client pipe struct + * + */ +static void pipe_clear(struct sps_pipe *pipe) +{ + INIT_LIST_HEAD(&pipe->list); + + pipe->state = 0; + pipe->pipe_index = SPS_BAM_PIPE_INVALID; + pipe->pipe_index_mask = 0; + pipe->irq_mask = 0; + pipe->mode = -1; + pipe->num_descs = 0; + pipe->desc_size = 0; + pipe->disconnecting = false; + pipe->late_eot = false; + memset(&pipe->sys, 0, sizeof(pipe->sys)); + INIT_LIST_HEAD(&pipe->sys.events_q); +} + +/** + * Allocate a BAM pipe + * + */ +u32 sps_bam_pipe_alloc(struct sps_bam *dev, u32 pipe_index) +{ + u32 pipe_mask; + + if (pipe_index == SPS_BAM_PIPE_INVALID) { + /* Allocate a pipe from the BAM */ + if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_ALLOC)) { + SPS_ERR(dev, + "sps:Restricted from allocating pipes on BAM %pa\n", + BAM_ID(dev)); + return SPS_BAM_PIPE_INVALID; + } + for (pipe_index = 0, pipe_mask = 1; + pipe_index < dev->props.num_pipes; + pipe_index++, pipe_mask <<= 1) { + if ((pipe_mask & dev->props.restricted_pipes)) + continue; /* This is a restricted pipe */ + + if (dev->pipes[pipe_index] == NULL) + break; /* Found an available pipe */ + } + if (pipe_index >= dev->props.num_pipes) { + SPS_ERR(dev, "sps:Fail to allocate pipe on BAM %pa\n", + BAM_ID(dev)); + return SPS_BAM_PIPE_INVALID; + } + } else { + /* Check that client-specified pipe is available */ + if (pipe_index >= dev->props.num_pipes || + pipe_index >= BAM_MAX_PIPES) { + SPS_ERR(dev, + "sps:Invalid pipe %d for allocate on BAM %pa\n", + pipe_index, BAM_ID(dev)); + return SPS_BAM_PIPE_INVALID; + } + if ((dev->props.restricted_pipes & (1UL << pipe_index))) { + SPS_ERR(dev, "sps:BAM %pa pipe %d is not local\n", + BAM_ID(dev), pipe_index); + return SPS_BAM_PIPE_INVALID; + } + if (dev->pipes[pipe_index] != NULL) { + SPS_ERR(dev, + "sps:Pipe %d already allocated on BAM %pa\n", + pipe_index, BAM_ID(dev)); + return SPS_BAM_PIPE_INVALID; + } + } + + /* Mark pipe as allocated */ + dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED; + + return pipe_index; +} + +/** + * Free a BAM pipe + * + */ +void sps_bam_pipe_free(struct sps_bam *dev, u32 pipe_index) +{ + struct sps_pipe *pipe; + + if (pipe_index >= dev->props.num_pipes) { + SPS_ERR(dev, "sps:Invalid BAM %pa pipe: %d\n", BAM_ID(dev), + pipe_index); + return; + } + + /* Get the client pipe struct and mark the pipe free */ + pipe = dev->pipes[pipe_index]; + dev->pipes[pipe_index] = NULL; + + /* Is the pipe currently allocated? */ + if (pipe == NULL) { + SPS_ERR(dev, + "sps:Attempt to free unallocated pipe %d on BAM %pa\n", + pipe_index, BAM_ID(dev)); + return; + } + + if (pipe == BAM_PIPE_UNASSIGNED) + return; /* Never assigned, so no work to do */ + + /* Return pending items to appropriate pools */ + if (!list_empty(&pipe->sys.events_q)) { + struct sps_q_event *sps_event; + + SPS_ERR(dev, + "sps:Disconnect BAM %pa pipe %d with events pending\n", + BAM_ID(dev), pipe_index); + + sps_event = list_entry((&pipe->sys.events_q)->next, + typeof(*sps_event), list); + + while (&sps_event->list != (&pipe->sys.events_q)) { + struct sps_q_event *sps_event_delete = sps_event; + + list_del(&sps_event->list); + sps_event = list_entry(sps_event->list.next, + typeof(*sps_event), list); + kfree(sps_event_delete); + } + } + + /* Clear the BAM pipe state struct */ + pipe_clear(pipe); +} + +/** + * Establish BAM pipe connection + * + */ +int sps_bam_pipe_connect(struct sps_pipe *bam_pipe, + const struct sps_bam_connect_param *params) +{ + struct bam_pipe_parameters hw_params; + struct sps_bam *dev; + const struct sps_connection *map = bam_pipe->map; + const struct sps_conn_end_pt *map_pipe; + const struct sps_conn_end_pt *other_pipe; + void *desc_buf = NULL; + u32 pipe_index; + int result; + + /* Clear the client pipe state and hw init struct */ + pipe_clear(bam_pipe); + memset(&hw_params, 0, sizeof(hw_params)); + + /* Initialize the BAM state struct */ + bam_pipe->mode = params->mode; + + /* Set pipe streaming mode */ + if ((params->options & SPS_O_STREAMING) == 0) + hw_params.stream_mode = BAM_STREAM_MODE_DISABLE; + else + hw_params.stream_mode = BAM_STREAM_MODE_ENABLE; + + /* Determine which end point to connect */ + if (bam_pipe->mode == SPS_MODE_SRC) { + map_pipe = &map->src; + other_pipe = &map->dest; + hw_params.dir = BAM_PIPE_PRODUCER; + } else { + map_pipe = &map->dest; + other_pipe = &map->src; + hw_params.dir = BAM_PIPE_CONSUMER; + } + + /* Process map parameters */ + dev = map_pipe->bam; + pipe_index = map_pipe->pipe_index; + + SPS_DBG2(dev, + "sps:BAM %pa; pipe %d; mode:%d; options:0x%x\n", + BAM_ID(dev), pipe_index, params->mode, params->options); + + if (pipe_index >= dev->props.num_pipes) { + SPS_ERR(dev, "sps:Invalid BAM %pa pipe: %d\n", BAM_ID(dev), + pipe_index); + return SPS_ERROR; + } + hw_params.event_threshold = (u16) map_pipe->event_threshold; + hw_params.ee = dev->props.ee; + hw_params.lock_group = map_pipe->lock_group; + + /* Verify that control of this pipe is allowed */ + if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_CTRL) || + (dev->props.restricted_pipes & (1UL << pipe_index))) { + SPS_ERR(dev, "sps:BAM %pa pipe %d is not local\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + /* Control without configuration permission is not supported yet */ + if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_CONFIG)) { + SPS_ERR(dev, + "sps:BAM %pa pipe %d remote config is not supported\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + /* Determine operational mode */ + if ((bam_pipe->connect.options & SPS_O_DUMMY_PEER) || + other_pipe->bam != NULL) { + unsigned long iova; + struct sps_bam *peer_bam; + /* BAM-to-BAM mode */ + bam_pipe->state |= BAM_STATE_BAM2BAM; + hw_params.mode = BAM_PIPE_MODE_BAM2BAM; + if (!(bam_pipe->connect.options & SPS_O_DUMMY_PEER)) + peer_bam = (struct sps_bam *)(other_pipe->bam); + if (dev->props.options & SPS_BAM_SMMU_EN) { + if (bam_pipe->mode == SPS_MODE_SRC) + iova = bam_pipe->connect.dest_iova; + else + iova = bam_pipe->connect.source_iova; + SPS_DBG2(dev, + "sps:BAM %pa pipe %d uses IOVA 0x%pK\n", + BAM_ID(dev), pipe_index, (void *)iova); + hw_params.peer_phys_addr = (u32)iova; + } else { + if (!(bam_pipe->connect.options & SPS_O_DUMMY_PEER)) + hw_params.peer_phys_addr = + peer_bam->props.phys_addr; + } + if (!(bam_pipe->connect.options & SPS_O_DUMMY_PEER)) { + hw_params.peer_pipe = other_pipe->pipe_index; + } else { + hw_params.peer_phys_addr = + bam_pipe->connect.destination; + hw_params.peer_pipe = + bam_pipe->connect.dest_pipe_index; + hw_params.dummy_peer = true; + } + /* Verify FIFO buffers are allocated for BAM-to-BAM pipes */ + if (map->desc.phys_base == SPS_ADDR_INVALID || + map->data.phys_base == SPS_ADDR_INVALID || + map->desc.size == 0 || map->data.size == 0) { + SPS_ERR(dev, + "sps:FIFO buffers are not allocated for BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + if (dev->props.options & SPS_BAM_SMMU_EN) { + hw_params.data_base = + (phys_addr_t)bam_pipe->connect.data.iova; + SPS_DBG2(dev, + "sps:BAM %pa pipe %d uses IOVA 0x%pK for data FIFO\n", + BAM_ID(dev), pipe_index, + (void *)(bam_pipe->connect.data.iova)); + } else { + hw_params.data_base = map->data.phys_base; + } + + hw_params.data_size = map->data.size; + + /* Clear the data FIFO for debug */ + if (map->data.base != NULL && bam_pipe->mode == SPS_MODE_SRC) + memset_io(map->data.base, 0, hw_params.data_size); + + /* set NWD bit for BAM2BAM producer pipe */ + if (bam_pipe->mode == SPS_MODE_SRC) { + if ((params->options & SPS_O_WRITE_NWD) == 0) + hw_params.write_nwd = BAM_WRITE_NWD_DISABLE; + else + hw_params.write_nwd = BAM_WRITE_NWD_ENABLE; + } + } else { + /* System mode */ + hw_params.mode = BAM_PIPE_MODE_SYSTEM; + bam_pipe->sys.desc_buf = map->desc.base; + bam_pipe->sys.desc_offset = 0; + bam_pipe->sys.acked_offset = 0; + } + + /* Initialize the client pipe state */ + bam_pipe->pipe_index = pipe_index; + bam_pipe->pipe_index_mask = 1UL << pipe_index; + + /* Get virtual address for descriptor FIFO */ + if (map->desc.phys_base != SPS_ADDR_INVALID) { + if (map->desc.size < (2 * sizeof(struct sps_iovec))) { + SPS_ERR(dev, + "sps:Invalid descriptor FIFO size for BAM %pa pipe %d: %d\n", + BAM_ID(dev), pipe_index, map->desc.size); + return SPS_ERROR; + } + desc_buf = map->desc.base; + + /* + * Note that descriptor base and size will be left zero from + * the memset() above if the physical address was invalid. + * This allows a satellite driver to set the FIFO as + * local memory for system mode. + */ + + if (dev->props.options & SPS_BAM_SMMU_EN) { + hw_params.desc_base = + (phys_addr_t)bam_pipe->connect.desc.iova; + SPS_DBG2(dev, + "sps:BAM %pa pipe %d uses IOVA 0x%pK for desc FIFO\n", + BAM_ID(dev), pipe_index, + (void *)(bam_pipe->connect.desc.iova)); + } else { + hw_params.desc_base = map->desc.phys_base; + } + + hw_params.desc_size = map->desc.size; + } + + /* Configure the descriptor FIFO for both operational modes */ + if (desc_buf != NULL) + if (bam_pipe->mode == SPS_MODE_SRC || + hw_params.mode == BAM_PIPE_MODE_SYSTEM) + memset_io(desc_buf, 0, hw_params.desc_size); + + bam_pipe->desc_size = hw_params.desc_size; + bam_pipe->num_descs = bam_pipe->desc_size / sizeof(struct sps_iovec); + + result = SPS_ERROR; + /* Insure that the BAM is enabled */ + if ((dev->state & BAM_STATE_ENABLED) == 0) + if (sps_bam_enable(dev)) + goto exit_init_err; + + /* Check pipe allocation */ + if (dev->pipes[pipe_index] != BAM_PIPE_UNASSIGNED) { + SPS_ERR(dev, "sps:Invalid pipe %d on BAM %pa for connect\n", + pipe_index, BAM_ID(dev)); + return SPS_ERROR; + } + + if (bam_pipe_is_enabled(&dev->base, pipe_index)) { + if (params->options & SPS_O_NO_DISABLE) + SPS_DBG2(dev, + "sps:BAM %pa pipe %d is already enabled\n", + BAM_ID(dev), pipe_index); + else { + SPS_ERR(dev, "sps:BAM %pa pipe %d sharing violation\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + } + + if (bam_pipe_init(&dev->base, pipe_index, &hw_params, dev->props.ee)) { + SPS_ERR(dev, "sps:BAM %pa pipe %d init error\n", + BAM_ID(dev), pipe_index); + goto exit_err; + } + + /* Assign pipe to client */ + dev->pipes[pipe_index] = bam_pipe; + + /* Process configuration parameters */ + if (params->options != 0 || + (bam_pipe->state & BAM_STATE_BAM2BAM) == 0) { + /* Process init-time only parameters */ + u32 irq_gen_addr; + + /* Set interrupt mode */ + irq_gen_addr = SPS_ADDR_INVALID; + if ((params->options & SPS_O_IRQ_MTI)) + /* Client has directly specified the MTI address */ + irq_gen_addr = params->irq_gen_addr; + else if ((dev->state & BAM_STATE_MTI)) + /* This BAM has MTI use enabled */ + irq_gen_addr = dev->props.irq_gen_addr; + + if (irq_gen_addr != SPS_ADDR_INVALID) { + /* + * No checks - assume BAM is already setup for + * MTI generation, + * or the pipe will be set to satellite control. + */ + bam_pipe->state |= BAM_STATE_MTI; + bam_pipe->irq_gen_addr = irq_gen_addr; + } + + /* Process runtime parameters */ + if (sps_bam_pipe_set_params(dev, pipe_index, + params->options)) { + dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED; + goto exit_err; + } + } + + /* Indicate initialization is complete */ + dev->pipes[pipe_index] = bam_pipe; + dev->pipe_active_mask |= 1UL << pipe_index; + list_add_tail(&bam_pipe->list, &dev->pipes_q); + + SPS_DBG2(dev, + "sps:BAM %pa; pipe %d; pipe_index_mask:0x%x; pipe_active_mask:0x%x\n", + BAM_ID(dev), pipe_index, + bam_pipe->pipe_index_mask, dev->pipe_active_mask); + + bam_pipe->state |= BAM_STATE_INIT; + result = 0; +exit_err: + if (result) { + if (params->options & SPS_O_NO_DISABLE) + SPS_DBG2(dev, "sps:BAM %pa pipe %d connection exits\n", + BAM_ID(dev), pipe_index); + else + bam_pipe_exit(&dev->base, pipe_index, dev->props.ee); + } +exit_init_err: + if (result) { + /* Clear the client pipe state */ + pipe_clear(bam_pipe); + } + + return result; +} + +/** + * Disconnect a BAM pipe connection + * + */ +int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index) +{ + struct sps_pipe *pipe; + int result; + unsigned long flags; + + if (pipe_index >= dev->props.num_pipes) { + SPS_ERR(dev, "sps:Invalid BAM %pa pipe: %d\n", BAM_ID(dev), + pipe_index); + return SPS_ERROR; + } + + /* Deallocate and reset the BAM pipe */ + pipe = dev->pipes[pipe_index]; + if (BAM_PIPE_IS_ASSIGNED(pipe)) { + if ((dev->pipe_active_mask & (1UL << pipe_index))) { + spin_lock_irqsave(&dev->isr_lock, flags); + list_del(&pipe->list); + dev->pipe_active_mask &= ~(1UL << pipe_index); + spin_unlock_irqrestore(&dev->isr_lock, flags); + } + dev->pipe_remote_mask &= ~(1UL << pipe_index); + if (pipe->connect.options & SPS_O_NO_DISABLE) + SPS_DBG2(dev, "sps:BAM %pa pipe %d exits\n", + BAM_ID(dev), pipe_index); + else + bam_pipe_exit(&dev->base, pipe_index, dev->props.ee); + if (pipe->sys.desc_cache != NULL) { + u32 size = pipe->num_descs * sizeof(void *); + + if (pipe->desc_size + size <= PAGE_SIZE) { + if (dev->props.options & SPS_BAM_HOLD_MEM) + memset(pipe->sys.desc_cache, 0, + pipe->desc_size + size); + else + kfree(pipe->sys.desc_cache); + } else { + vfree(pipe->sys.desc_cache); + } + pipe->sys.desc_cache = NULL; + } + dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED; + pipe_clear(pipe); + result = 0; + } else { + result = SPS_ERROR; + } + + if (result) + SPS_ERR(dev, "sps:BAM %pa pipe %d already disconnected\n", + BAM_ID(dev), pipe_index); + + return result; +} + +/** + * Set BAM pipe interrupt enable state + * + * This function sets the interrupt enable state for a BAM pipe. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @poll - true if SPS_O_POLL is set, false otherwise + * + */ +static void pipe_set_irq(struct sps_bam *dev, u32 pipe_index, + u32 poll) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + enum bam_enable irq_enable; + + SPS_DBG2(dev, + "sps:BAM:%pa; pipe %d; poll:%d, irq_mask:0x%x; pipe state:0x%x; dev state:0x%x\n", + BAM_ID(dev), pipe_index, poll, pipe->irq_mask, + pipe->state, dev->state); + + if (poll == 0 && pipe->irq_mask != 0 && + (dev->state & BAM_STATE_IRQ)) { + if ((pipe->state & BAM_STATE_BAM2BAM) != 0 && + (pipe->state & BAM_STATE_IRQ) == 0) { + /* + * If enabling the interrupt for a BAM-to-BAM pipe, + * clear the existing interrupt status + */ + (void)bam_pipe_get_and_clear_irq_status(&dev->base, + pipe_index); + } + pipe->state |= BAM_STATE_IRQ; + irq_enable = BAM_ENABLE; + pipe->polled = false; + } else { + pipe->state &= ~BAM_STATE_IRQ; + irq_enable = BAM_DISABLE; + pipe->polled = true; + if (poll == 0 && pipe->irq_mask) + SPS_DBG2(dev, + "sps:BAM %pa pipe %d forced to use polling\n", + BAM_ID(dev), pipe_index); + } + if ((pipe->state & BAM_STATE_MTI) == 0) + bam_pipe_set_irq(&dev->base, pipe_index, irq_enable, + pipe->irq_mask, dev->props.ee); + else + bam_pipe_set_mti(&dev->base, pipe_index, irq_enable, + pipe->irq_mask, pipe->irq_gen_addr); + +} + +/** + * Set BAM pipe parameters + * + */ +int sps_bam_pipe_set_params(struct sps_bam *dev, u32 pipe_index, u32 options) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + u32 mask; + int wake_up_is_one_shot; + int no_queue; + int ack_xfers; + u32 size; + int n; + + SPS_DBG2(dev, "sps:BAM %pa pipe %d opt 0x%x\n", + BAM_ID(dev), pipe_index, options); + + /* Capture some options */ + wake_up_is_one_shot = ((options & SPS_O_WAKEUP_IS_ONESHOT)); + no_queue = ((options & SPS_O_NO_Q)); + ack_xfers = ((options & SPS_O_ACK_TRANSFERS)); + + pipe->hybrid = options & SPS_O_HYBRID; + pipe->late_eot = options & SPS_O_LATE_EOT; + + /* Create interrupt source mask */ + mask = 0; + for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) { + /* Is client registering for this event? */ + if ((options & opt_event_table[n].option) == 0) + continue; /* No */ + + mask |= opt_event_table[n].pipe_irq; + } + +#ifdef SPS_BAM_STATISTICS + /* Is an illegal mode change specified? */ + if (pipe->sys.desc_wr_count > 0 && + (no_queue != pipe->sys.no_queue + || ack_xfers != pipe->sys.ack_xfers)) { + SPS_ERR(dev, + "sps:Queue/ack mode change after transfer: BAM %pa pipe %d opt 0x%x\n", + BAM_ID(dev), pipe_index, options); + return SPS_ERROR; + } +#endif /* SPS_BAM_STATISTICS */ + + /* Is client setting invalid options for a BAM-to-BAM connection? */ + if ((pipe->state & BAM_STATE_BAM2BAM) && + (options & BAM2BAM_O_INVALID)) { + SPS_ERR(dev, + "sps:Invalid option for BAM-to-BAM: BAM %pa pipe %d opt 0x%x\n", + BAM_ID(dev), pipe_index, options); + return SPS_ERROR; + } + + /* Allocate descriptor FIFO cache if NO_Q option is disabled */ + if (!no_queue && pipe->sys.desc_cache == NULL && pipe->num_descs > 0 + && (pipe->state & BAM_STATE_BAM2BAM) == 0) { + /* Allocate both descriptor cache and user pointer array */ + size = pipe->num_descs * sizeof(void *); + + if (pipe->desc_size + size <= PAGE_SIZE) { + if ((dev->props.options & + SPS_BAM_HOLD_MEM)) { + if (dev->desc_cache_pointers[pipe_index]) { + pipe->sys.desc_cache = + dev->desc_cache_pointers + [pipe_index]; + } else { + pipe->sys.desc_cache = + kzalloc(pipe->desc_size + size, + GFP_KERNEL); + dev->desc_cache_pointers[pipe_index] = + pipe->sys.desc_cache; + } + } else { + pipe->sys.desc_cache = + kzalloc(pipe->desc_size + size, + GFP_KERNEL); + } + if (pipe->sys.desc_cache == NULL) { + SPS_ERR(dev, + "sps:No memory for pipe%d of BAM %pa\n", + pipe_index, BAM_ID(dev)); + return -ENOMEM; + } + } else { + pipe->sys.desc_cache = + vzalloc(pipe->desc_size + size); + + if (pipe->sys.desc_cache == NULL) { + SPS_ERR(dev, + "sps:No memory for pipe %d of BAM %pa\n", + pipe_index, BAM_ID(dev)); + return -ENOMEM; + } + + } + + if (pipe->sys.desc_cache == NULL) { + /*** MUST BE LAST POINT OF FAILURE (see below) *****/ + SPS_ERR(dev, + "sps:Desc cache error: BAM %pa pipe %d: %d\n", + BAM_ID(dev), pipe_index, + pipe->desc_size + size); + return SPS_ERROR; + } + pipe->sys.user_ptrs = (void **)(pipe->sys.desc_cache + + pipe->desc_size); + pipe->sys.cache_offset = pipe->sys.acked_offset; + } + + /* + * No failures beyond this point. Note that malloc() is last point of + * failure, so no free() handling is needed. + */ + + /* Enable/disable the pipe's interrupt sources */ + pipe->irq_mask = mask; + pipe_set_irq(dev, pipe_index, (options & SPS_O_POLL)); + + /* Store software feature enables */ + pipe->wake_up_is_one_shot = wake_up_is_one_shot; + pipe->sys.no_queue = no_queue; + pipe->sys.ack_xfers = ack_xfers; + + return 0; +} + +/** + * Enable a BAM pipe + * + */ +int sps_bam_pipe_enable(struct sps_bam *dev, u32 pipe_index) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + + /* Enable the BAM pipe */ + bam_pipe_enable(&dev->base, pipe_index); + pipe->state |= BAM_STATE_ENABLED; + + return 0; +} + +/** + * Disable a BAM pipe + * + */ +int sps_bam_pipe_disable(struct sps_bam *dev, u32 pipe_index) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + + /* Disable the BAM pipe */ + if (pipe->connect.options & SPS_O_NO_DISABLE) + SPS_DBG2(dev, "sps:BAM %pa pipe %d enters disable state\n", + BAM_ID(dev), pipe_index); + else + bam_pipe_disable(&dev->base, pipe_index); + + pipe->state &= ~BAM_STATE_ENABLED; + + return 0; +} + +/** + * Register an event for a BAM pipe + * + */ +int sps_bam_pipe_reg_event(struct sps_bam *dev, + u32 pipe_index, + struct sps_register_event *reg) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + struct sps_bam_event_reg *event_reg; + int n; + + if (pipe->sys.no_queue && reg->xfer_done != NULL && + reg->mode != SPS_TRIGGER_CALLBACK) { + SPS_ERR(dev, + "sps:Only callback events support for NO_Q: BAM %pa pipe %d mode %d\n", + BAM_ID(dev), pipe_index, reg->mode); + return SPS_ERROR; + } + + for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) { + int index; + + /* Is client registering for this event? */ + if ((reg->options & opt_event_table[n].option) == 0) + continue; /* No */ + + index = SPS_EVENT_INDEX(opt_event_table[n].event_id); + if (index < 0) + SPS_ERR(dev, + "sps:Negative event index: BAM %pa pipe %d mode %d\n", + BAM_ID(dev), pipe_index, reg->mode); + else { + event_reg = &pipe->sys.event_regs[index]; + event_reg->xfer_done = reg->xfer_done; + event_reg->callback = reg->callback; + event_reg->mode = reg->mode; + event_reg->user = reg->user; + } + } + + return 0; +} + +/** + * Submit a transfer of a single buffer to a BAM pipe + * + */ +int sps_bam_pipe_transfer_one(struct sps_bam *dev, + u32 pipe_index, u32 addr, u32 size, + void *user, u32 flags) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + struct sps_iovec *desc; + struct sps_iovec iovec; + u32 next_write; + static int show_recom; + + SPS_DBG(dev, "sps:BAM %pa pipe %d addr 0x%pK size 0x%x flags 0x%x\n", + BAM_ID(dev), pipe_index, + (void *)(long)addr, size, flags); + + /* Is this a BAM-to-BAM or satellite connection? */ + if ((pipe->state & (BAM_STATE_BAM2BAM | BAM_STATE_REMOTE))) { + SPS_ERR(dev, "sps:Transfer on BAM-to-BAM: BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + /* + * Client identifier (user pointer) is not supported for + * SPS_O_NO_Q option. + */ + if (pipe->sys.no_queue && user != NULL) { + SPS_ERR(dev, "sps:User pointer arg non-NULL: BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + /* Determine if descriptor can be queued */ + next_write = pipe->sys.desc_offset + sizeof(struct sps_iovec); + if (next_write >= pipe->desc_size) + next_write = 0; + + if (next_write == pipe->sys.acked_offset) { + /* + * If pipe is polled and client is not ACK'ing descriptors, + * perform polling operation so that any outstanding ACKs + * can occur. + */ + if (!pipe->sys.ack_xfers && pipe->polled) { + pipe_handler_eot(dev, pipe); + if (next_write == pipe->sys.acked_offset) { + if (!show_recom) { + show_recom = true; + SPS_ERR(dev, + "sps:Client of BAM %pa pipe %d is recommended to have flow control\n", + BAM_ID(dev), pipe_index); + } + + SPS_DBG1(dev, + "sps:Descriptor FIFO is full for BAM %pa pipe %d after pipe_handler_eot\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + } else { + if (!show_recom) { + show_recom = true; + SPS_ERR(dev, + "sps:Client of BAM %pa pipe %d is recommended to have flow control\n", + BAM_ID(dev), pipe_index); + } + + SPS_DBG1(dev, + "sps:Descriptor FIFO is full for BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + } + + /* Create descriptor */ + if (!pipe->sys.no_queue) + desc = (struct sps_iovec *) (pipe->sys.desc_cache + + pipe->sys.desc_offset); + else + desc = &iovec; + + desc->addr = addr; + desc->size = size; + + if ((flags & SPS_IOVEC_FLAG_DEFAULT) == 0) { + desc->flags = (flags & BAM_IOVEC_FLAG_MASK) + | DESC_UPPER_ADDR(flags); + } else { + if (pipe->mode == SPS_MODE_SRC) + desc->flags = SPS_IOVEC_FLAG_INT + | DESC_UPPER_ADDR(flags); + else + desc->flags = (SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT) + | DESC_UPPER_ADDR(flags); + } + +#ifdef SPS_BAM_STATISTICS + if ((flags & SPS_IOVEC_FLAG_INT)) + pipe->sys.int_flags++; + if ((flags & SPS_IOVEC_FLAG_EOT)) + pipe->sys.eot_flags++; +#endif /* SPS_BAM_STATISTICS */ + + /* Update hardware descriptor FIFO - should result in burst */ + *((struct sps_iovec *) (pipe->sys.desc_buf + pipe->sys.desc_offset)) + = *desc; + + /* Record user pointer value */ + if (!pipe->sys.no_queue) { + u32 index = pipe->sys.desc_offset / sizeof(struct sps_iovec); + + pipe->sys.user_ptrs[index] = user; +#ifdef SPS_BAM_STATISTICS + if (user != NULL) + pipe->sys.user_ptrs_count++; +#endif /* SPS_BAM_STATISTICS */ + } + + /* Update descriptor ACK offset */ + pipe->sys.desc_offset = next_write; + +#ifdef SPS_BAM_STATISTICS + /* Update statistics */ + pipe->sys.desc_wr_count++; +#endif /* SPS_BAM_STATISTICS */ + + /* Notify pipe */ + if ((flags & SPS_IOVEC_FLAG_NO_SUBMIT) == 0) { + wmb(); /* Memory Barrier */ + bam_pipe_set_desc_write_offset(&dev->base, pipe_index, + next_write); + } + + if (dev->ipc_loglevel == 0) + SPS_DBG(dev, + "sps: BAM phy addr:%pa; pipe %d; write pointer to tell HW: 0x%x; write pointer read from HW: 0x%x\n", + BAM_ID(dev), pipe_index, next_write, + bam_pipe_get_desc_write_offset(&dev->base, pipe_index)); + + return 0; +} + +/** + * Submit a transfer to a BAM pipe + * + */ +int sps_bam_pipe_transfer(struct sps_bam *dev, + u32 pipe_index, struct sps_transfer *transfer) +{ + struct sps_iovec *iovec; + u32 count; + u32 flags; + void *user; + int n; + int result; + struct sps_pipe *pipe = dev->pipes[pipe_index]; + + if (transfer->iovec_count == 0) { + SPS_ERR(dev, "sps:iovec count zero: BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + if (!pipe->sys.ack_xfers && pipe->polled) { + sps_bam_pipe_get_unused_desc_num(dev, pipe_index, + &count); + count = pipe->desc_size / sizeof(struct sps_iovec) - count - 1; + } else + sps_bam_get_free_count(dev, pipe_index, &count); + + if (count < transfer->iovec_count) { + SPS_ERR(dev, + "sps:Insufficient free desc: BAM %pa pipe %d: %d\n", + BAM_ID(dev), pipe_index, count); + return SPS_ERROR; + } + + user = NULL; /* NULL for all except last descriptor */ + for (n = (int)transfer->iovec_count - 1, iovec = transfer->iovec; + n >= 0; n--, iovec++) { + if (n > 0) { + /* This is *not* the last descriptor */ + flags = iovec->flags | SPS_IOVEC_FLAG_NO_SUBMIT; + } else { + /* This *is* the last descriptor */ + flags = iovec->flags; + user = transfer->user; + } + result = sps_bam_pipe_transfer_one(dev, pipe_index, + iovec->addr, + iovec->size, user, + flags); + if (result) + return SPS_ERROR; + } + + return 0; +} + +int sps_bam_pipe_inject_zlt(struct sps_bam *dev, u32 pipe_index) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + struct sps_iovec *desc; + u32 read_p, write_p, next_write; + + if (pipe->state & BAM_STATE_BAM2BAM) + SPS_DBG2(dev, "sps: BAM-to-BAM pipe: BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + else + SPS_DBG2(dev, "sps: BAM-to-System pipe: BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + + if (!(pipe->state & BAM_STATE_ENABLED)) { + SPS_ERR(dev, + "sps: BAM %pa pipe %d is not enabled\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + read_p = bam_pipe_get_desc_read_offset(&dev->base, pipe_index); + write_p = bam_pipe_get_desc_write_offset(&dev->base, pipe_index); + + SPS_DBG2(dev, + "sps: BAM %pa pipe %d: read pointer:0x%x; write pointer:0x%x\n", + BAM_ID(dev), pipe_index, read_p, write_p); + + if (read_p == write_p) { + SPS_ERR(dev, + "sps: BAM %pa pipe %d: read pointer 0x%x is already equal to write pointer\n", + BAM_ID(dev), pipe_index, read_p); + return SPS_ERROR; + } + + next_write = write_p + sizeof(struct sps_iovec); + if (next_write >= pipe->desc_size) { + SPS_DBG2(dev, + "sps: BAM %pa pipe %d: next write is 0x%x: wrap around\n", + BAM_ID(dev), pipe_index, next_write); + next_write = 0; + } + + desc = (struct sps_iovec *) (pipe->connect.desc.base + write_p); + desc->addr = 0; + desc->size = 0; + desc->flags = SPS_IOVEC_FLAG_EOT; + + bam_pipe_set_desc_write_offset(&dev->base, pipe_index, + next_write); + wmb(); /* update write pointer in HW */ + SPS_DBG2(dev, + "sps: BAM %pa pipe %d: write pointer to tell HW: 0x%x; write pointer read from HW: 0x%x\n", + BAM_ID(dev), pipe_index, next_write, + bam_pipe_get_desc_write_offset(&dev->base, pipe_index)); + + return 0; +} + +/** + * Allocate an event tracking struct + * + * This function allocates an event tracking struct. + * + * @pipe - pointer to pipe state + * + * @event_reg - pointer to event registration + * + * @return - pointer to event notification struct, or NULL + * + */ +static struct sps_q_event *alloc_event(struct sps_pipe *pipe, + struct sps_bam_event_reg *event_reg) +{ + struct sps_q_event *event; + + /* A callback event object is registered, so trigger with payload */ + event = &pipe->sys.event; + memset(event, 0, sizeof(*event)); + + return event; +} + +/** + * Trigger an event notification + * + * This function triggers an event notification. + * + * @dev - pointer to BAM device descriptor + * + * @pipe - pointer to pipe state + * + * @event_reg - pointer to event registration + * + * @sps_event - pointer to event struct + * + */ +static void trigger_event(struct sps_bam *dev, + struct sps_pipe *pipe, + struct sps_bam_event_reg *event_reg, + struct sps_q_event *sps_event) +{ + if (sps_event == NULL) { + SPS_DBG1(dev, "%s", "sps:trigger_event.sps_event is NULL\n"); + return; + } + + if (event_reg->xfer_done) { + complete(event_reg->xfer_done); + SPS_DBG(dev, "sps: done=%d\n", event_reg->xfer_done->done); + } + + if (event_reg->callback) { + SPS_DBG(dev, "%s", "sps:trigger_event.using callback\n"); + event_reg->callback(&sps_event->notify); + } + +} + +/** + * Handle a BAM pipe's generic interrupt sources + * + * This function creates the event notification for a BAM pipe's + * generic interrupt sources. The caller of this function must lock the BAM + * device's mutex. + * + * @dev - pointer to BAM device descriptor + * + * @pipe - pointer to pipe state + * + * @event_id - event identifier enum + * + */ +static void pipe_handler_generic(struct sps_bam *dev, + struct sps_pipe *pipe, + enum sps_event event_id) +{ + struct sps_bam_event_reg *event_reg; + struct sps_q_event *sps_event; + int index; + + index = SPS_EVENT_INDEX(event_id); + if (index < 0 || index >= SPS_EVENT_INDEX(SPS_EVENT_MAX)) + return; + + event_reg = &pipe->sys.event_regs[index]; + sps_event = alloc_event(pipe, event_reg); + if (sps_event != NULL) { + sps_event->notify.event_id = event_id; + sps_event->notify.user = event_reg->user; + trigger_event(dev, pipe, event_reg, sps_event); + } +} + +/** + * Handle a BAM pipe's WAKEUP interrupt sources + * + * This function creates the event notification for a BAM pipe's + * WAKEUP interrupt source. The caller of this function must lock the BAM + * device's mutex. + * + * @dev - pointer to BAM device descriptor + * + * @pipe - pointer to pipe state + * + */ +static void pipe_handler_wakeup(struct sps_bam *dev, struct sps_pipe *pipe) +{ + struct sps_bam_event_reg *event_reg; + struct sps_q_event *event; + u32 pipe_index = pipe->pipe_index; + + if (pipe->wake_up_is_one_shot) { + SPS_DBG2(dev, + "sps:BAM:%pa pipe %d wake_up_is_one_shot; irq_mask:0x%x\n", + BAM_ID(dev), pipe_index, pipe->irq_mask); + /* Disable the pipe WAKEUP interrupt source */ + pipe->irq_mask &= ~BAM_PIPE_IRQ_WAKE; + pipe_set_irq(dev, pipe_index, pipe->polled); + } + + event_reg = &pipe->sys.event_regs[SPS_EVENT_INDEX(SPS_EVENT_WAKEUP)]; + event = alloc_event(pipe, event_reg); + if (event != NULL) { + event->notify.event_id = SPS_EVENT_WAKEUP; + event->notify.user = event_reg->user; + trigger_event(dev, pipe, event_reg, event); + } +} + +/** + * Handle a BAM pipe's EOT/INT interrupt sources + * + * This function creates the event notification for a BAM pipe's EOT interrupt + * source. The caller of this function must lock the BAM device's mutex. + * + * @dev - pointer to BAM device descriptor + * + * @pipe - pointer to pipe state + * + */ +static void pipe_handler_eot(struct sps_bam *dev, struct sps_pipe *pipe) +{ + struct sps_bam_event_reg *event_reg; + struct sps_q_event *event; + struct sps_iovec *desc; + struct sps_iovec *cache; + void **user; + u32 *update_offset; + u32 pipe_index = pipe->pipe_index; + u32 offset; + u32 end_offset; + enum sps_event event_id; + u32 flags; + u32 enabled; + int producer = (pipe->mode == SPS_MODE_SRC); + + if (pipe->sys.handler_eot) { + /* + * This can happen if the pipe is configured for polling + * (IRQ disabled) and callback event generation. + * The client may perform a get_iovec() inside the callback. + */ + SPS_DBG(dev, + "sps: still handling EOT for pipe %d\n", + pipe->pipe_index); + return; + } + + pipe->sys.handler_eot = true; + + /* Get offset of last descriptor completed by the pipe */ + end_offset = bam_pipe_get_desc_read_offset(&dev->base, pipe_index); + + if (dev->ipc_loglevel == 0) + SPS_DBG(dev, + "sps: pipe index:%d; read pointer:0x%x; write pointer:0x%x; sys.acked_offset:0x%x\n", + pipe->pipe_index, end_offset, + bam_pipe_get_desc_write_offset(&dev->base, pipe_index), + pipe->sys.acked_offset); + + if (producer && pipe->late_eot) { + struct sps_iovec *desc_end; + + if (end_offset == 0) + desc_end = (struct sps_iovec *)(pipe->sys.desc_buf + + pipe->desc_size - sizeof(struct sps_iovec)); + else + desc_end = (struct sps_iovec *) (pipe->sys.desc_buf + + end_offset - sizeof(struct sps_iovec)); + + if (!(desc_end->flags & SPS_IOVEC_FLAG_EOT)) { + if (end_offset == 0) + end_offset = pipe->desc_size + - sizeof(struct sps_iovec); + else + end_offset -= sizeof(struct sps_iovec); + } + } + + /* If no queue, then do not generate any events */ + if (pipe->sys.no_queue) { + if (!pipe->sys.ack_xfers) { + /* Client is not ACK'ing transfers, so do it now */ + pipe->sys.acked_offset = end_offset; + } + pipe->sys.handler_eot = false; + SPS_DBG(dev, + "sps: pipe %d has no queue\n", pipe->pipe_index); + return; + } + + /* + * Get offset of last descriptor processed by software, + * and update to the last descriptor completed by the pipe + */ + if (!pipe->sys.ack_xfers) { + update_offset = &pipe->sys.acked_offset; + offset = *update_offset; + } else { + update_offset = &pipe->sys.cache_offset; + offset = *update_offset; + } + + /* Are there any completed descriptors to process? */ + if (offset == end_offset) { + pipe->sys.handler_eot = false; + SPS_DBG(dev, + "sps: there is no completed desc to process for pipe %d\n", + pipe->pipe_index); + return; + } + + /* Determine enabled events */ + enabled = 0; + if ((pipe->irq_mask & SPS_O_EOT)) + enabled |= SPS_IOVEC_FLAG_EOT; + + if ((pipe->irq_mask & SPS_O_DESC_DONE)) + enabled |= SPS_IOVEC_FLAG_INT; + + /* + * For producer pipe, update the cached descriptor byte count and flags. + * For consumer pipe, the BAM does not update the descriptors, so just + * use the cached copies. + */ + if (producer) { + /* + * Do copies in a tight loop to increase chance of + * multi-descriptor burst accesses on the bus + */ + struct sps_iovec *desc_end; + + /* Set starting point for copy */ + desc = (struct sps_iovec *) (pipe->sys.desc_buf + offset); + cache = (struct sps_iovec *) (pipe->sys.desc_cache + offset); + + /* Fetch all completed descriptors to end of FIFO (wrap) */ + if (end_offset < offset) { + desc_end = (struct sps_iovec *) + (pipe->sys.desc_buf + pipe->desc_size); + while (desc < desc_end) + *cache++ = *desc++; + + desc = (void *)pipe->sys.desc_buf; + cache = (void *)pipe->sys.desc_cache; + } + + /* Fetch all remaining completed descriptors (no wrap) */ + desc_end = (struct sps_iovec *) (pipe->sys.desc_buf + + end_offset); + while (desc < desc_end) + *cache++ = *desc++; + } + + /* Process all completed descriptors */ + cache = (struct sps_iovec *) (pipe->sys.desc_cache + offset); + user = &pipe->sys.user_ptrs[offset / sizeof(struct sps_iovec)]; + for (;;) { + SPS_DBG(dev, + "sps: pipe index:%d; iovec addr:0x%pK; size:0x%x; flags:0x%x; enabled:0x%x; *user is %s NULL\n", + pipe->pipe_index, (void *)(long)cache->addr, + cache->size, cache->flags, enabled, + (*user == NULL) ? "" : "not"); + + /* + * Increment offset to next descriptor and update pipe offset + * so a client callback can fetch the I/O vector. + */ + offset += sizeof(struct sps_iovec); + if (offset >= pipe->desc_size) + /* Roll to start of descriptor FIFO */ + offset = 0; + + *update_offset = offset; +#ifdef SPS_BAM_STATISTICS + pipe->sys.desc_rd_count++; +#endif /* SPS_BAM_STATISTICS */ + + /* Did client request notification for this descriptor? */ + flags = cache->flags & enabled; + if (*user != NULL || flags) { + int index; + + if ((flags & SPS_IOVEC_FLAG_EOT)) + event_id = SPS_EVENT_EOT; + else + event_id = SPS_EVENT_DESC_DONE; + + index = SPS_EVENT_INDEX(event_id); + event_reg = &pipe->sys.event_regs[index]; + event = alloc_event(pipe, event_reg); + if (event != NULL) { + /* + * Store the descriptor and user pointer + * in the notification + */ + event->notify.data.transfer.iovec = *cache; + event->notify.data.transfer.user = *user; + + event->notify.event_id = event_id; + event->notify.user = event_reg->user; + trigger_event(dev, pipe, event_reg, event); + } else { + SPS_ERR(dev, + "sps: pipe %d: event is NULL\n", + pipe->pipe_index); + } +#ifdef SPS_BAM_STATISTICS + if (*user != NULL) + pipe->sys.user_found++; +#endif /* SPS_BAM_STATISTICS */ + } + + /* Increment to next descriptor */ + if (offset == end_offset) + break; /* No more descriptors */ + + if (offset) { + cache++; + user++; + } else { + cache = (void *)pipe->sys.desc_cache; + user = pipe->sys.user_ptrs; + } + } + + pipe->sys.handler_eot = false; +} + +/** + * Handle a BAM pipe's interrupt sources + * + * This function handles a BAM pipe's interrupt sources. + * The caller of this function must lock the BAM device's mutex. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return void + * + */ +static void pipe_handler(struct sps_bam *dev, struct sps_pipe *pipe) +{ + u32 pipe_index; + u32 status; + enum sps_event event_id; + + /* Get interrupt sources and ack all */ + pipe_index = pipe->pipe_index; + status = bam_pipe_get_and_clear_irq_status(&dev->base, pipe_index); + + SPS_DBG(dev, "sps: bam %pa.pipe %d.status=0x%x\n", + BAM_ID(dev), pipe_index, status); + + /* Check for enabled interrupt sources */ + status &= pipe->irq_mask; + if (status == 0) + /* No enabled interrupt sources are active */ + return; + + /* + * Process the interrupt sources in order of frequency of occurrance. + * Check for early exit opportunities. + */ + + if ((status & (SPS_O_EOT | SPS_O_DESC_DONE)) && + (pipe->state & BAM_STATE_BAM2BAM) == 0) { + pipe_handler_eot(dev, pipe); + if (pipe->sys.no_queue) { + /* + * EOT handler will not generate any event if there + * is no queue, + * so generate "empty" (no descriptor) event + */ + if ((status & SPS_O_EOT)) + event_id = SPS_EVENT_EOT; + else + event_id = SPS_EVENT_DESC_DONE; + + pipe_handler_generic(dev, pipe, event_id); + } + status &= ~(SPS_O_EOT | SPS_O_DESC_DONE); + if (status == 0) + return; + } + + if ((status & SPS_O_WAKEUP)) { + pipe_handler_wakeup(dev, pipe); + status &= ~SPS_O_WAKEUP; + if (status == 0) + return; + } + + if ((status & SPS_O_INACTIVE)) { + pipe_handler_generic(dev, pipe, SPS_EVENT_INACTIVE); + status &= ~SPS_O_INACTIVE; + if (status == 0) + return; + } + + if ((status & SPS_O_OUT_OF_DESC)) { + pipe_handler_generic(dev, pipe, + SPS_EVENT_OUT_OF_DESC); + status &= ~SPS_O_OUT_OF_DESC; + if (status == 0) + return; + } + + if ((status & SPS_O_RST_ERROR) && enhd_pipe) { + SPS_ERR(dev, "sps:bam %pa ;pipe 0x%x irq status=0x%x\n" + "sps: BAM_PIPE_IRQ_RST_ERROR\n", + BAM_ID(dev), pipe_index, status); + bam_output_register_content(&dev->base, dev->props.ee); + pipe_handler_generic(dev, pipe, + SPS_EVENT_RST_ERROR); + status &= ~SPS_O_RST_ERROR; + if (status == 0) + return; + } + + if ((status & SPS_O_HRESP_ERROR) && enhd_pipe) { + SPS_ERR(dev, "sps:bam %pa ;pipe 0x%x irq status=0x%x\n" + "sps: BAM_PIPE_IRQ_HRESP_ERROR\n", + BAM_ID(dev), pipe_index, status); + bam_output_register_content(&dev->base, dev->props.ee); + pipe_handler_generic(dev, pipe, + SPS_EVENT_HRESP_ERROR); + status &= ~SPS_O_HRESP_ERROR; + if (status == 0) + return; + } + + if ((status & SPS_EVENT_ERROR)) + pipe_handler_generic(dev, pipe, SPS_EVENT_ERROR); +} + +/** + * Get a BAM pipe event + * + */ +int sps_bam_pipe_get_event(struct sps_bam *dev, + u32 pipe_index, struct sps_event_notify *notify) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + struct sps_q_event *event_queue; + + if (pipe->sys.no_queue) { + SPS_ERR(dev, + "sps:Invalid connection for event: BAM %pa pipe %d context 0x%pK\n", + BAM_ID(dev), pipe_index, pipe); + notify->event_id = SPS_EVENT_INVALID; + return SPS_ERROR; + } + + /* If pipe is polled, perform polling operation */ + if (pipe->polled && (pipe->state & BAM_STATE_BAM2BAM) == 0) + pipe_handler_eot(dev, pipe); + + /* Pull an event off the synchronous event queue */ + if (list_empty(&pipe->sys.events_q)) { + event_queue = NULL; + SPS_DBG(dev, "sps:events_q of bam %pa is empty\n", + BAM_ID(dev)); + } else { + SPS_DBG(dev, "sps:events_q of bam %pa is not empty\n", + BAM_ID(dev)); + event_queue = + list_first_entry(&pipe->sys.events_q, struct sps_q_event, + list); + list_del(&event_queue->list); + } + + /* Update client's event buffer */ + if (event_queue == NULL) { + /* No event queued, so set client's event to "invalid" */ + notify->event_id = SPS_EVENT_INVALID; + } else { + /* + * Copy event into client's buffer and return the event + * to the pool + */ + *notify = event_queue->notify; + kfree(event_queue); +#ifdef SPS_BAM_STATISTICS + pipe->sys.get_events++; +#endif /* SPS_BAM_STATISTICS */ + } + + return 0; +} + +/** + * Get processed I/O vector + */ +int sps_bam_pipe_get_iovec(struct sps_bam *dev, u32 pipe_index, + struct sps_iovec *iovec) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + struct sps_iovec *desc; + u32 read_offset; + + /* Is this a valid pipe configured for get_iovec use? */ + if (!pipe->sys.ack_xfers || + (pipe->state & BAM_STATE_BAM2BAM) != 0 || + (pipe->state & BAM_STATE_REMOTE)) { + return SPS_ERROR; + } + + /* If pipe is polled and queue is enabled, perform polling operation */ + if ((pipe->polled || pipe->hybrid) && !pipe->sys.no_queue) { + SPS_DBG(dev, + "sps: BAM: %pa; pipe index:%d; polled is %d; hybrid is %d\n", + BAM_ID(dev), pipe_index, + pipe->polled, pipe->hybrid); + pipe_handler_eot(dev, pipe); + } + + /* Is there a completed descriptor? */ + if (pipe->sys.no_queue) + read_offset = + bam_pipe_get_desc_read_offset(&dev->base, pipe_index); + else + read_offset = pipe->sys.cache_offset; + + if (read_offset == pipe->sys.acked_offset) { + /* No, so clear the iovec to indicate FIFO is empty */ + memset(iovec, 0, sizeof(*iovec)); + SPS_DBG(dev, + "sps: BAM: %pa; pipe index:%d; no iovec to process\n", + BAM_ID(dev), pipe_index); + return 0; + } + + /* Fetch next descriptor */ + desc = (struct sps_iovec *) (pipe->sys.desc_buf + + pipe->sys.acked_offset); + *iovec = *desc; +#ifdef SPS_BAM_STATISTICS + pipe->sys.get_iovecs++; +#endif /* SPS_BAM_STATISTICS */ + + /* Update read/ACK offset */ + pipe->sys.acked_offset += sizeof(struct sps_iovec); + if (pipe->sys.acked_offset >= pipe->desc_size) + pipe->sys.acked_offset = 0; + + SPS_DBG(dev, + "sps: pipe index:%d; iovec addr:0x%pK; size:0x%x; flags:0x%x; acked_offset:0x%x\n", + pipe->pipe_index, (void *)(long)desc->addr, + desc->size, desc->flags, pipe->sys.acked_offset); + + return 0; +} + +/** + * Determine whether a BAM pipe descriptor FIFO is empty + * + */ +int sps_bam_pipe_is_empty(struct sps_bam *dev, u32 pipe_index, + u32 *empty) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + u32 end_offset; + u32 acked_offset; + + /* Is this a satellite connection? */ + if ((pipe->state & BAM_STATE_REMOTE)) { + SPS_ERR(dev, "sps:Is empty on remote: BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + /* Get offset of last descriptor completed by the pipe */ + end_offset = bam_pipe_get_desc_read_offset(&dev->base, pipe_index); + + if ((pipe->state & BAM_STATE_BAM2BAM) == 0) + /* System mode */ + acked_offset = pipe->sys.acked_offset; + else + /* BAM-to-BAM */ + acked_offset = bam_pipe_get_desc_write_offset(&dev->base, + pipe_index); + + + /* Determine descriptor FIFO state */ + if (end_offset == acked_offset) { + *empty = true; + } else { + if ((pipe->state & BAM_STATE_BAM2BAM) == 0) { + *empty = false; + SPS_DBG1(dev, + "sps: pipe index:%d; this sys2bam pipe is NOT empty\n", + pipe->pipe_index); + return 0; + } + if (bam_pipe_check_zlt(&dev->base, pipe_index)) { + bool p_idc; + u32 next_write; + + p_idc = bam_pipe_check_pipe_empty(&dev->base, + pipe_index); + + next_write = acked_offset + sizeof(struct sps_iovec); + if (next_write >= pipe->desc_size) + next_write = 0; + + if (next_write == end_offset) { + *empty = true; + if (!p_idc) + SPS_DBG3(dev, + "sps:BAM %pa pipe %d pipe empty checking for ZLT\n", + BAM_ID(dev), pipe_index); + } else { + *empty = false; + } + } else { + *empty = false; + } + } + + SPS_DBG1(dev, + "sps: pipe index:%d; this pipe is %s empty\n", + pipe->pipe_index, *empty ? "" : "NOT"); + + return 0; +} + +/** + * Get number of free slots in a BAM pipe descriptor FIFO + * + */ +int sps_bam_get_free_count(struct sps_bam *dev, u32 pipe_index, + u32 *count) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + u32 next_write; + u32 free; + + /* Is this a BAM-to-BAM or satellite connection? */ + if ((pipe->state & (BAM_STATE_BAM2BAM | BAM_STATE_REMOTE))) { + SPS_ERR(dev, + "sps:Free count on BAM-to-BAM or remote: BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + *count = 0; + return SPS_ERROR; + } + + /* Determine descriptor FIFO state */ + next_write = pipe->sys.desc_offset + sizeof(struct sps_iovec); + if (next_write >= pipe->desc_size) + next_write = 0; + + if (pipe->sys.acked_offset >= next_write) + free = pipe->sys.acked_offset - next_write; + else + free = pipe->desc_size - next_write + pipe->sys.acked_offset; + + free /= sizeof(struct sps_iovec); + *count = free; + + return 0; +} + +/** + * Set BAM pipe to satellite ownership + * + */ +int sps_bam_set_satellite(struct sps_bam *dev, u32 pipe_index) +{ + struct sps_pipe *pipe = dev->pipes[pipe_index]; + + /* + * Switch to satellite control is only supported on processor + * that controls the BAM global config on multi-EE BAMs + */ + if ((dev->props.manage & SPS_BAM_MGR_MULTI_EE) == 0 || + (dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE)) { + SPS_ERR(dev, + "sps:Cannot grant satellite control to BAM %pa pipe %d\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + /* Is this pipe locally controlled? */ + if ((dev->pipe_active_mask & (1UL << pipe_index)) == 0) { + SPS_ERR(dev, "sps:BAM %pa pipe %d not local and active\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + /* Disable local interrupts for this pipe */ + if (!pipe->polled) + bam_pipe_set_irq(&dev->base, pipe_index, BAM_DISABLE, + pipe->irq_mask, dev->props.ee); + + if (BAM_VERSION_MTI_SUPPORT(dev->version)) { + /* + * Set pipe to MTI interrupt mode. + * Must be performed after IRQ disable, + * because it is necessary to re-enable the IRQ to enable + * MTI generation. + * Set both pipe IRQ mask and MTI dest address to zero. + */ + if ((pipe->state & BAM_STATE_MTI) == 0 || pipe->polled) { + bam_pipe_satellite_mti(&dev->base, pipe_index, 0, + dev->props.ee); + pipe->state |= BAM_STATE_MTI; + } + } + + /* Indicate satellite control */ + list_del(&pipe->list); + dev->pipe_active_mask &= ~(1UL << pipe_index); + dev->pipe_remote_mask |= pipe->pipe_index_mask; + pipe->state |= BAM_STATE_REMOTE; + + return 0; +} + +/** + * Perform BAM pipe timer control + * + */ +int sps_bam_pipe_timer_ctrl(struct sps_bam *dev, + u32 pipe_index, + struct sps_timer_ctrl *timer_ctrl, + struct sps_timer_result *timer_result) +{ + enum bam_pipe_timer_mode mode; + int result = 0; + + /* Is this pipe locally controlled? */ + if ((dev->pipe_active_mask & (1UL << pipe_index)) == 0) { + SPS_ERR(dev, "sps:BAM %pa pipe %d not local and active\n", + BAM_ID(dev), pipe_index); + return SPS_ERROR; + } + + /* Perform the timer operation */ + switch (timer_ctrl->op) { + case SPS_TIMER_OP_CONFIG: + mode = (timer_ctrl->mode == SPS_TIMER_MODE_ONESHOT) ? + BAM_PIPE_TIMER_ONESHOT : + BAM_PIPE_TIMER_PERIODIC; + bam_pipe_timer_config(&dev->base, pipe_index, mode, + timer_ctrl->timeout_msec * 8); + break; + case SPS_TIMER_OP_RESET: + bam_pipe_timer_reset(&dev->base, pipe_index); + break; + case SPS_TIMER_OP_READ: + break; + default: + result = SPS_ERROR; + break; + } + + /* Provide the current timer value */ + if (timer_result != NULL) + timer_result->current_timer = + bam_pipe_timer_get_count(&dev->base, pipe_index); + + return result; +} + +/** + * Get the number of unused descriptors in the descriptor FIFO + * of a pipe + */ +int sps_bam_pipe_get_unused_desc_num(struct sps_bam *dev, u32 pipe_index, + u32 *desc_num) +{ + u32 sw_offset, peer_offset, fifo_size; + u32 desc_size = sizeof(struct sps_iovec); + struct sps_pipe *pipe = dev->pipes[pipe_index]; + + if (pipe == NULL) + return SPS_ERROR; + + fifo_size = pipe->desc_size; + + sw_offset = bam_pipe_get_desc_read_offset(&dev->base, pipe_index); + if ((dev->props.options & SPS_BAM_CACHED_WP) && + !(pipe->state & BAM_STATE_BAM2BAM)) { + peer_offset = pipe->sys.desc_offset; + SPS_DBG(dev, + "sps:BAM %pa pipe %d: peer offset in cache:0x%x\n", + BAM_ID(dev), pipe_index, peer_offset); + } else { + peer_offset = bam_pipe_get_desc_write_offset(&dev->base, + pipe_index); + } + + if (sw_offset <= peer_offset) + *desc_num = (peer_offset - sw_offset) / desc_size; + else + *desc_num = (peer_offset + fifo_size - sw_offset) / desc_size; + + return 0; +} + +/* + * Check if a pipe of a BAM has any pending descriptor + */ +bool sps_bam_pipe_pending_desc(struct sps_bam *dev, u32 pipe_index) +{ + u32 sw_offset, peer_offset; + + sw_offset = bam_pipe_get_desc_read_offset(&dev->base, pipe_index); + peer_offset = bam_pipe_get_desc_write_offset(&dev->base, pipe_index); + + if (sw_offset == peer_offset) + return false; + else + return true; +} diff --git a/drivers/soc/qcom/sps/sps_bam.h b/drivers/soc/qcom/sps/sps_bam.h new file mode 100644 index 000000000000..d687f0334f68 --- /dev/null +++ b/drivers/soc/qcom/sps/sps_bam.h @@ -0,0 +1,608 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011-2019, 2021, The Linux Foundation. All rights reserved. + */ +/* + * Function and data structure declarations for SPS BAM handling. + */ + + +#ifndef _SPSBAM_H_ +#define _SPSBAM_H_ + +#include +#include +#include +#include +#include + +#include "spsi.h" + +#define BAM_HANDLE_INVALID 0 + +#define to_sps_bam_dev(x) \ + container_of((x), struct sps_bam, base) + +enum bam_irq { + BAM_DEV_IRQ_RDY_TO_SLEEP = 0x00000001, + BAM_DEV_IRQ_HRESP_ERROR = 0x00000002, + BAM_DEV_IRQ_ERROR = 0x00000004, + BAM_DEV_IRQ_TIMER = 0x00000010, +}; + +/* Pipe interrupt mask */ +enum bam_pipe_irq { + /* BAM finishes descriptor which has INT bit selected */ + BAM_PIPE_IRQ_DESC_INT = 0x00000001, + /* Inactivity timer Expires */ + BAM_PIPE_IRQ_TIMER = 0x00000002, + /* Wakeup peripheral (i.e. USB) */ + BAM_PIPE_IRQ_WAKE = 0x00000004, + /* Producer - no free space for adding a descriptor */ + /* Consumer - no descriptors for processing */ + BAM_PIPE_IRQ_OUT_OF_DESC = 0x00000008, + /* Pipe Error interrupt */ + BAM_PIPE_IRQ_ERROR = 0x00000010, + /* End-Of-Transfer */ + BAM_PIPE_IRQ_EOT = 0x00000020, + /* Pipe RESET unsuccessful */ + BAM_PIPE_IRQ_RST_ERROR = 0x00000040, + /* Errorneous Hresponse by AHB MASTER */ + BAM_PIPE_IRQ_HRESP_ERROR = 0x00000080, +}; + +/* Halt Type */ +enum bam_halt { + BAM_HALT_OFF = 0, + BAM_HALT_ON = 1, +}; + +/* Threshold values of the DMA channels */ +enum bam_dma_thresh_dma { + BAM_DMA_THRESH_512 = 0x3, + BAM_DMA_THRESH_256 = 0x2, + BAM_DMA_THRESH_128 = 0x1, + BAM_DMA_THRESH_64 = 0x0, +}; + +/* Weight values of the DMA channels */ +enum bam_dma_weight_dma { + BAM_DMA_WEIGHT_HIGH = 7, + BAM_DMA_WEIGHT_MED = 3, + BAM_DMA_WEIGHT_LOW = 1, + BAM_DMA_WEIGHT_DEFAULT = BAM_DMA_WEIGHT_LOW, + BAM_DMA_WEIGHT_DISABLE = 0, +}; + + +/* Invalid pipe index value */ +#define SPS_BAM_PIPE_INVALID ((u32)(-1)) + +/* Parameters for sps_bam_pipe_connect() */ +struct sps_bam_connect_param { + /* which end point must be initialized */ + enum sps_mode mode; + + /* OR'd connection end point options (see SPS_O defines) */ + u32 options; + + /* SETPEND/MTI interrupt generation parameters */ + u32 irq_gen_addr; + u32 irq_gen_data; + +}; + +/* Event registration struct */ +struct sps_bam_event_reg { + /* Client's event object handle */ + struct completion *xfer_done; + void (*callback)(struct sps_event_notify *notify); + + /* Event trigger mode */ + enum sps_trigger mode; + + /* User pointer that will be provided in event payload data */ + void *user; + +}; + +/* Descriptor FIFO cache entry */ +struct sps_bam_desc_cache { + struct sps_iovec iovec; + void *user; /* User pointer registered with this transfer */ +}; + +/* Forward declaration */ +struct sps_bam; + +/* System mode control */ +struct sps_bam_sys_mode { + /* Descriptor FIFO control */ + u8 *desc_buf; /* Descriptor FIFO for BAM pipe */ + u32 desc_offset; /* Next new descriptor to be written to hardware */ + u32 acked_offset; /* Next descriptor to be retired by software */ + + /* Descriptor cache control (!no_queue only) */ + u8 *desc_cache; /* Software cache of descriptor FIFO contents */ + u32 cache_offset; /* Next descriptor to be cached (ack_xfers only) */ + + /* User pointers associated with cached descriptors */ + void **user_ptrs; + + /* Event handling */ + struct sps_bam_event_reg event_regs[SPS_EVENT_INDEX(SPS_EVENT_MAX)]; + struct list_head events_q; + + struct sps_q_event event; /* Temp storage for event creation */ + int no_queue; /* Whether events are queued */ + int ack_xfers; /* Whether client must ACK all descriptors */ + int handler_eot; /* Whether EOT handling is in progress (debug) */ + + /* Statistics */ +#ifdef SPS_BAM_STATISTICS + u32 desc_wr_count; + u32 desc_rd_count; + u32 user_ptrs_count; + u32 user_found; + u32 int_flags; + u32 eot_flags; + u32 callback_events; + u32 wait_events; + u32 queued_events; + u32 get_events; + u32 get_iovecs; +#endif /* SPS_BAM_STATISTICS */ +}; + +/* BAM pipe descriptor */ +struct sps_pipe { + struct list_head list; + + /* Client state */ + u32 client_state; + struct sps_bam *bam; + struct sps_connect connect; + const struct sps_connection *map; + + /* Pipe parameters */ + u32 state; + u32 pipe_index; + u32 pipe_index_mask; + u32 irq_mask; + int polled; + int hybrid; + bool late_eot; + u32 irq_gen_addr; + enum sps_mode mode; + u32 num_descs; /* Size (number of elements) of descriptor FIFO */ + u32 desc_size; /* Size (bytes) of descriptor FIFO */ + int wake_up_is_one_shot; /* Whether WAKEUP event is a one-shot or not */ + + /* System mode control */ + struct sps_bam_sys_mode sys; + + bool disconnecting; +}; + +/* BAM device descriptor */ +struct sps_bam { + struct list_head list; + + /* BAM device properties, including connection defaults */ + struct sps_bam_props props; + + /* BAM device state */ + u32 state; + struct mutex lock; + void __iomem *base; /* BAM virtual base address */ + u32 version; + spinlock_t isr_lock; + spinlock_t connection_lock; + unsigned long irqsave_flags; + + /* Pipe state */ + u32 pipe_active_mask; + u32 pipe_remote_mask; + struct sps_pipe *pipes[BAM_MAX_PIPES]; + struct list_head pipes_q; + + /* Statistics */ + u32 irq_from_disabled_pipe; + u32 event_trigger_failures; + + void *ipc_log0; + void *ipc_log1; + void *ipc_log2; + void *ipc_log3; + void *ipc_log4; + + u32 ipc_loglevel; + + /* Desc cache pointers */ + u8 *desc_cache_pointers[BAM_MAX_PIPES]; +}; + +/** + * BAM driver initialization + * + * This function initializes the BAM driver. + * + * @options - driver options bitflags (see SPS_OPT_*) + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_driver_init(u32 options); + +/** + * BAM device initialization + * + * This function initializes a BAM device. + * + * @dev - pointer to BAM device descriptor + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_device_init(struct sps_bam *dev); + +/** + * BAM device de-initialization + * + * This function de-initializes a BAM device. + * + * @dev - pointer to BAM device descriptor + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_device_de_init(struct sps_bam *dev); + +/** + * BAM device reset + * + * This Function resets a BAM device. + * + * @dev - pointer to BAM device descriptor + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_reset(struct sps_bam *dev); + +/** + * BAM device enable + * + * This function enables a BAM device. + * + * @dev - pointer to BAM device descriptor + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_enable(struct sps_bam *dev); + +/** + * BAM device disable + * + * This Function disables a BAM device. + * + * @dev - pointer to BAM device descriptor + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_disable(struct sps_bam *dev); + +/** + * Allocate a BAM pipe + * + * This function allocates a BAM pipe. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - client-specified pipe index, or SPS_BAM_PIPE_INVALID if + * any available pipe is acceptable + * + * @return - allocated pipe index, or SPS_BAM_PIPE_INVALID on error + * + */ +u32 sps_bam_pipe_alloc(struct sps_bam *dev, u32 pipe_index); + +/** + * Free a BAM pipe + * + * This function frees a BAM pipe. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + */ +void sps_bam_pipe_free(struct sps_bam *dev, u32 pipe_index); + +/** + * Establish BAM pipe connection + * + * This function establishes a connection for a BAM pipe (end point). + * + * @client - pointer to client pipe state struct + * + * @params - connection parameters + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_connect(struct sps_pipe *client, + const struct sps_bam_connect_param *params); + +/** + * Disconnect a BAM pipe connection + * + * This function disconnects a connection for a BAM pipe (end point). + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index); + +/** + * Set BAM pipe parameters + * + * This function sets parameters for a BAM pipe. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @options - bitflag options (see SPS_O_*) + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_set_params(struct sps_bam *dev, u32 pipe_index, u32 options); + +/** + * Enable a BAM pipe + * + * This function enables a BAM pipe. Note that this function + * is separate from the pipe connect function to allow proper + * sequencing of consumer enable followed by producer enable. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_enable(struct sps_bam *dev, u32 pipe_index); + +/** + * Disable a BAM pipe + * + * This function disables a BAM pipe. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_disable(struct sps_bam *dev, u32 pipe_index); + +/** + * Register an event for a BAM pipe + * + * This function registers an event for a BAM pipe. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @reg - pointer to event registration struct + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_reg_event(struct sps_bam *dev, u32 pipe_index, + struct sps_register_event *reg); + +/** + * Submit a transfer of a single buffer to a BAM pipe + * + * This function submits a transfer of a single buffer to a BAM pipe. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @addr - physical address of buffer to transfer + * + * @size - number of bytes to transfer + * + * @user - user pointer to register for event + * + * @flags - descriptor flags (see SPS_IOVEC_FLAG defines) + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_transfer_one(struct sps_bam *dev, u32 pipe_index, u32 addr, + u32 size, void *user, u32 flags); + +/** + * Submit a transfer to a BAM pipe + * + * This function submits a transfer to a BAM pipe. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @transfer - pointer to transfer struct + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_transfer(struct sps_bam *dev, u32 pipe_index, + struct sps_transfer *transfer); + +/** + * Get a BAM pipe event + * + * This function polls for a BAM pipe event. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @notify - pointer to event notification struct + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_get_event(struct sps_bam *dev, u32 pipe_index, + struct sps_event_notify *notify); + +/** + * Get processed I/O vector + * + * This function fetches the next processed I/O vector. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @iovec - Pointer to I/O vector struct (output). + * This struct will be zeroed if there are no more processed I/O vectors. + * + * @return 0 on success, negative value on error + */ +int sps_bam_pipe_get_iovec(struct sps_bam *dev, u32 pipe_index, + struct sps_iovec *iovec); + +/** + * Determine whether a BAM pipe descriptor FIFO is empty + * + * This function returns the empty state of a BAM pipe descriptor FIFO. + * + * The pipe mutex must be locked before calling this function. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @empty - pointer to client's empty status word (boolean) + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_is_empty(struct sps_bam *dev, u32 pipe_index, u32 *empty); + +/** + * Get number of free slots in a BAM pipe descriptor FIFO + * + * This function returns the number of free slots in a BAM pipe descriptor FIFO. + * + * The pipe mutex must be locked before calling this function. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @count - pointer to count status + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_get_free_count(struct sps_bam *dev, u32 pipe_index, u32 *count); + +/** + * Set BAM pipe to satellite ownership + * + * This function sets the BAM pipe to satellite ownership. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_set_satellite(struct sps_bam *dev, u32 pipe_index); + +/** + * Perform BAM pipe timer control + * + * This function performs BAM pipe timer control operations. + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @timer_ctrl - Pointer to timer control specification + * + * @timer_result - Pointer to buffer for timer operation result. + * This argument can be NULL if no result is expected for the operation. + * If non-NULL, the current timer value will always provided. + * + * @return 0 on success, negative value on error + * + */ +int sps_bam_pipe_timer_ctrl(struct sps_bam *dev, u32 pipe_index, + struct sps_timer_ctrl *timer_ctrl, + struct sps_timer_result *timer_result); + + +/** + * Get the number of unused descriptors in the descriptor FIFO + * of a pipe + * + * @dev - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @desc_num - number of unused descriptors + * + */ +int sps_bam_pipe_get_unused_desc_num(struct sps_bam *dev, u32 pipe_index, + u32 *desc_num); + +/* + * sps_bam_check_irq - check IRQ of a BAM device. + * @dev - pointer to BAM device descriptor + * + * This function checks any pending interrupt of a BAM device. + * + * Return: 0 on success, negative value on error + */ +int sps_bam_check_irq(struct sps_bam *dev); + +/* + * sps_bam_pipe_pending_desc - checking pending descriptor. + * @dev: BAM device handle + * @pipe_index: pipe index + * + * This function checks if a pipe of a BAM has any pending descriptor. + * + * @return true if there is any desc pending + */ +bool sps_bam_pipe_pending_desc(struct sps_bam *dev, u32 pipe_index); + +/* + * sps_bam_pipe_inject_zlt - inject a ZLT with EOT. + * @dev: BAM device handle + * @pipe_index: pipe index + * + * This function injects a ZLT with EOT for a pipe of a BAM. + * + * Return: 0 on success, negative value on error + */ +int sps_bam_pipe_inject_zlt(struct sps_bam *dev, u32 pipe_index); +#endif /* _SPSBAM_H_ */ diff --git a/drivers/soc/qcom/sps/sps_core.h b/drivers/soc/qcom/sps/sps_core.h new file mode 100644 index 000000000000..cb06920796c2 --- /dev/null +++ b/drivers/soc/qcom/sps/sps_core.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011, 2013, 2015-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/* + * Function and data structure declarations. + */ + +#ifndef _SPS_CORE_H_ +#define _SPS_CORE_H_ + +#include +#include +#include + +#include "spsi.h" +#include "sps_bam.h" + +/* Connection state definitions */ +#define SPS_STATE_DEF(x) ('S' | ('P' << 8) | ('S' << 16) | ((x) << 24)) +#define IS_SPS_STATE_OK(x) \ + (((x)->client_state & 0x00ffffff) == SPS_STATE_DEF(0)) + +/* Configuration indicating satellite connection */ +#define SPS_CONFIG_SATELLITE 0x11111111 + +/* Client connection state */ +#define SPS_STATE_DISCONNECT 0 +#define SPS_STATE_ALLOCATE SPS_STATE_DEF(1) +#define SPS_STATE_CONNECT SPS_STATE_DEF(2) +#define SPS_STATE_ENABLE SPS_STATE_DEF(3) +#define SPS_STATE_DISABLE SPS_STATE_DEF(4) + + +/** + * Find the BAM device from the handle + * + * This function finds a BAM device in the BAM registration list that + * matches the specified device handle. + * + * @h - device handle of the BAM + * + * @return - pointer to the BAM device struct, or NULL on error + * + */ +struct sps_bam *sps_h2bam(unsigned long h); + +/** + * Initialize resource manager module + * + * This function initializes the resource manager module. + * + * @rm - pointer to resource manager struct + * + * @options - driver options bitflags (see SPS_OPT_*) + * + * @return 0 on success, negative value on error + * + */ +int sps_rm_init(struct sps_rm *rm, u32 options); + +/** + * De-initialize resource manager module + * + * This function de-initializes the resource manager module. + * + */ +void sps_rm_de_init(void); + +/** + * Initialize client state context + * + * This function initializes a client state context struct. + * + * @connect - pointer to client connection state struct + * + */ +void sps_rm_config_init(struct sps_connect *connect); + +/** + * Process connection state change + * + * This function processes a connection state change. + * + * @pipe - pointer to pipe context + * + * @state - new state for connection + * + * @return 0 on success, negative value on error + * + */ +int sps_rm_state_change(struct sps_pipe *pipe, u32 state); + +#endif /* _SPS_CORE_H_ */ diff --git a/drivers/soc/qcom/sps/sps_dma.c b/drivers/soc/qcom/sps/sps_dma.c new file mode 100644 index 000000000000..f5c3a9ecd8cf --- /dev/null +++ b/drivers/soc/qcom/sps/sps_dma.c @@ -0,0 +1,921 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011-2013, 2015, 2017-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/* BAM-DMA Manager. */ + +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + +#include +#include + +#include "spsi.h" +#include "bam.h" +#include "sps_bam.h" +#include "sps_core.h" + +/** + * registers + */ + +#define DMA_ENBL (0x00000000) +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM +#define DMA_REVISION (0x00000004) +#define DMA_CONFIG (0x00000008) +#define DMA_CHNL_CONFIG(n) (0x00001000 + 4096 * (n)) +#else +#define DMA_CHNL_CONFIG(n) (0x00000004 + 4 * (n)) +#define DMA_CONFIG (0x00000040) +#endif + +/** + * masks + */ + +/* DMA_CHNL_confign */ +#ifdef CONFIG_SPS_SUPPORT_NDP_BAM +#define DMA_CHNL_PRODUCER_PIPE_ENABLED 0x40000 +#define DMA_CHNL_CONSUMER_PIPE_ENABLED 0x20000 +#endif +#define DMA_CHNL_HALT_DONE 0x10000 +#define DMA_CHNL_HALT 0x1000 +#define DMA_CHNL_ENABLE 0x100 +#define DMA_CHNL_ACT_THRESH 0x30 +#define DMA_CHNL_WEIGHT 0x7 + +/* DMA_CONFIG */ +#define TESTBUS_SELECT 0x3 + +/** + * + * Write register with debug info. + * + * @base - bam base virtual address. + * @offset - register offset. + * @val - value to write. + * + */ +static inline void dma_write_reg(void *base, u32 offset, u32 val) +{ + iowrite32(val, base + offset); + SPS_DBG(sps, "sps:bamdma: write reg 0x%x w_val 0x%x\n", offset, val); +} + +/** + * Write register masked field with debug info. + * + * @base - bam base virtual address. + * @offset - register offset. + * @mask - register bitmask. + * @val - value to write. + * + */ +static inline void dma_write_reg_field(void *base, u32 offset, + const u32 mask, u32 val) +{ + unsigned long lmask = mask; + u32 shift = find_first_bit(&lmask, 32); + u32 tmp = ioread32(base + offset); + + tmp &= ~mask; /* clear written bits */ + val = tmp | (val << shift); + iowrite32(val, base + offset); + SPS_DBG(sps, "sps:bamdma: write reg 0x%x w_val 0x%x\n", offset, val); +} + +/* Round max number of pipes to nearest multiple of 2 */ +#define DMA_MAX_PIPES ((BAM_MAX_PIPES / 2) * 2) + +/* Maximum number of BAM-DMAs supported */ +#define MAX_BAM_DMA_DEVICES 1 + +/* Maximum number of BAMs that will be registered */ +#define MAX_BAM_DMA_BAMS 1 + +/* Pipe enable check values */ +#define DMA_PIPES_STATE_DIFF 0 +#define DMA_PIPES_BOTH_DISABLED 1 +#define DMA_PIPES_BOTH_ENABLED 2 + +/* Even pipe is tx/dest/input/write, odd pipe is rx/src/output/read */ +#define DMA_PIPE_IS_DEST(p) (((p) & 1) == 0) +#define DMA_PIPE_IS_SRC(p) (((p) & 1) != 0) + +/* BAM DMA pipe state */ +enum bamdma_pipe_state { + PIPE_INACTIVE = 0, + PIPE_ACTIVE +}; + +/* BAM DMA channel state */ +enum bamdma_chan_state { + DMA_CHAN_STATE_FREE = 0, + DMA_CHAN_STATE_ALLOC_EXT, /* Client allocation */ + DMA_CHAN_STATE_ALLOC_INT /* Internal (resource mgr) allocation */ +}; + +struct bamdma_chan { + /* Allocation state */ + enum bamdma_chan_state state; + + /* BAM DMA channel configuration parameters */ + u32 threshold; + enum sps_dma_priority priority; + + /* HWIO channel configuration parameters */ + enum bam_dma_thresh_dma thresh; + enum bam_dma_weight_dma weight; + +}; + +/* BAM DMA device state */ +struct bamdma_device { + /* BAM-DMA device state */ + int enabled; + int local; + + /* BAM device state */ + struct sps_bam *bam; + + /* BAM handle, for deregistration */ + unsigned long h; + + /* BAM DMA device virtual mapping */ + void *virt_addr; + int virtual_mapped; + phys_addr_t phys_addr; + void *hwio; + + /* BAM DMA pipe/channel state */ + u32 num_pipes; + enum bamdma_pipe_state pipes[DMA_MAX_PIPES]; + struct bamdma_chan chans[DMA_MAX_PIPES / 2]; + +}; + +/* BAM-DMA devices */ +static struct bamdma_device bam_dma_dev[MAX_BAM_DMA_DEVICES]; +static struct mutex bam_dma_lock; + +/* + * The BAM DMA module registers all BAMs in the BSP properties, but only + * uses the first BAM-DMA device for allocations. References to the others + * are stored in the following data array. + */ +static int num_bams; +static unsigned long bam_handles[MAX_BAM_DMA_BAMS]; + +/** + * Find BAM-DMA device + * + * This function finds the BAM-DMA device associated with the BAM handle. + * + * @h - BAM handle + * + * @return - pointer to BAM-DMA device, or NULL on error + * + */ +static struct bamdma_device *sps_dma_find_device(unsigned long h) +{ + return &bam_dma_dev[0]; +} + +/** + * BAM DMA device enable + * + * This function enables a BAM DMA device and the associated BAM. + * + * @dev - pointer to BAM DMA device context + * + * @return 0 on success, negative value on error + * + */ +static int sps_dma_device_enable(struct bamdma_device *dev) +{ + if (dev->enabled) + return 0; + + /* + * If the BAM-DMA device is locally controlled then enable BAM-DMA + * device + */ + if (dev->local) + dma_write_reg(dev->virt_addr, DMA_ENBL, 1); + + /* Enable BAM device */ + if (sps_bam_enable(dev->bam)) { + SPS_ERR(sps, "sps:Failed to enable BAM DMA's BAM: %pa\n", + &dev->phys_addr); + return SPS_ERROR; + } + + dev->enabled = true; + + return 0; +} + +/** + * BAM DMA device enable + * + * This function initializes a BAM DMA device. + * + * @dev - pointer to BAM DMA device context + * + * @return 0 on success, negative value on error + * + */ +static int sps_dma_device_disable(struct bamdma_device *dev) +{ + u32 pipe_index; + + if (!dev->enabled) + return 0; + + /* Do not disable if channels active */ + for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) { + if (dev->pipes[pipe_index] != PIPE_INACTIVE) + break; + } + + if (pipe_index < dev->num_pipes) { + SPS_ERR(sps, + "sps:Fail to disable BAM-DMA %pa:channels are active\n", + &dev->phys_addr); + return SPS_ERROR; + } + + dev->enabled = false; + + /* Disable BAM device */ + if (sps_bam_disable(dev->bam)) { + SPS_ERR(sps, + "sps:Fail to disable BAM-DMA BAM:%pa\n", + &dev->phys_addr); + return SPS_ERROR; + } + + /* Is the BAM-DMA device locally controlled? */ + if (dev->local) + /* Disable BAM-DMA device */ + dma_write_reg(dev->virt_addr, DMA_ENBL, 0); + + return 0; +} + +/** + * Initialize BAM DMA device + * + */ +int sps_dma_device_init(unsigned long h) +{ + struct bamdma_device *dev; + struct sps_bam_props *props; + int result = SPS_ERROR; + + mutex_lock(&bam_dma_lock); + + /* Find a free BAM-DMA device slot */ + dev = NULL; + if (bam_dma_dev[0].bam != NULL) { + SPS_ERR(sps, + "sps:%s:BAM-DMA BAM device is already initialized\n", + __func__); + goto exit_err; + } else { + dev = &bam_dma_dev[0]; + } + + /* Record BAM */ + memset(dev, 0, sizeof(*dev)); + dev->h = h; + dev->bam = sps_h2bam(h); + + if (dev->bam == NULL) { + SPS_ERR(sps, + "sps:%s:BAM-DMA BAM device is not found from the handle\n", + __func__); + goto exit_err; + } + + /* Map the BAM DMA device into virtual space, if necessary */ + props = &dev->bam->props; + dev->phys_addr = props->periph_phys_addr; + if (props->periph_virt_addr != NULL) { + dev->virt_addr = props->periph_virt_addr; + dev->virtual_mapped = false; + } else { + if (props->periph_virt_size == 0) { + SPS_ERR(sps, + "sps:Unable to map BAM DMA IO memory: %pa %x\n", + &dev->phys_addr, props->periph_virt_size); + goto exit_err; + } + + dev->virt_addr = ioremap(dev->phys_addr, + props->periph_virt_size); + if (dev->virt_addr == NULL) { + SPS_ERR(sps, + "sps:Unable to map BAM DMA IO memory: %pa %x\n", + &dev->phys_addr, props->periph_virt_size); + goto exit_err; + } + dev->virtual_mapped = true; + } + dev->hwio = (void *) dev->virt_addr; + + /* Is the BAM-DMA device locally controlled? */ + if ((props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) { + SPS_DBG3(sps, "sps:BAM-DMA is controlled locally: %pa\n", + &dev->phys_addr); + dev->local = true; + } else { + SPS_DBG3(sps, "sps:BAM-DMA is controlled remotely: %pa\n", + &dev->phys_addr); + dev->local = false; + } + + /* + * Enable the BAM DMA and determine the number of pipes/channels. + * Leave the BAM-DMA enabled, since it is always a shared device. + */ + if (sps_dma_device_enable(dev)) + goto exit_err; + + dev->num_pipes = dev->bam->props.num_pipes; + + result = 0; +exit_err: + if (result) { + if (dev != NULL) { + if (dev->virtual_mapped) + iounmap(dev->virt_addr); + + dev->bam = NULL; + } + } + + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * De-initialize BAM DMA device + * + */ +int sps_dma_device_de_init(unsigned long h) +{ + struct bamdma_device *dev; + u32 pipe_index; + u32 chan; + int result = 0; + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device(h); + if (dev == NULL) { + SPS_ERR(sps, "sps:BAM-DMA: not registered: %pK\n", (void *)h); + result = SPS_ERROR; + goto exit_err; + } + + /* Check for channel leaks */ + for (chan = 0; chan < dev->num_pipes / 2; chan++) { + if (dev->chans[chan].state != DMA_CHAN_STATE_FREE) { + SPS_ERR(sps, "sps:BAM-DMA: channel not free: %d\n", + chan); + result = SPS_ERROR; + dev->chans[chan].state = DMA_CHAN_STATE_FREE; + } + } + for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) { + if (dev->pipes[pipe_index] != PIPE_INACTIVE) { + SPS_ERR(sps, "sps:BAM-DMA: pipe not inactive: %d\n", + pipe_index); + result = SPS_ERROR; + dev->pipes[pipe_index] = PIPE_INACTIVE; + } + } + + /* Disable BAM and BAM-DMA */ + if (sps_dma_device_disable(dev)) + result = SPS_ERROR; + + dev->h = BAM_HANDLE_INVALID; + dev->bam = NULL; + if (dev->virtual_mapped) + iounmap(dev->virt_addr); + +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * Initialize BAM DMA module + * + */ +int sps_dma_init(const struct sps_bam_props *bam_props) +{ + struct sps_bam_props props; + const struct sps_bam_props *bam_reg; + unsigned long h; + + /* Init local data */ + memset(&bam_dma_dev, 0, sizeof(bam_dma_dev)); + num_bams = 0; + memset(bam_handles, 0, sizeof(bam_handles)); + + /* Create a mutex to control access to the BAM-DMA devices */ + mutex_init(&bam_dma_lock); + + /* Are there any BAM DMA devices? */ + if (bam_props == NULL) + return 0; + + /* + * Registers all BAMs in the BSP properties, but only uses the first + * BAM-DMA device for allocations. + */ + if (bam_props->phys_addr) { + /* Force multi-EE option for all BAM-DMAs */ + bam_reg = bam_props; + if ((bam_props->options & SPS_BAM_OPT_BAMDMA) && + (bam_props->manage & SPS_BAM_MGR_MULTI_EE) == 0) { + SPS_DBG(sps, + "sps:Setting multi-EE options for BAM-DMA: %pa\n", + &bam_props->phys_addr); + props = *bam_props; + props.manage |= SPS_BAM_MGR_MULTI_EE; + bam_reg = &props; + } + + /* Register the BAM */ + if (sps_register_bam_device(bam_reg, &h)) { + SPS_ERR(sps, + "sps:Fail to register BAM-DMA BAM device: phys %pa\n", + &bam_props->phys_addr); + return SPS_ERROR; + } + + /* Record the BAM so that it may be deregistered later */ + if (num_bams < MAX_BAM_DMA_BAMS) { + bam_handles[num_bams] = h; + num_bams++; + } else { + SPS_ERR(sps, "sps:BAM-DMA: BAM limit exceeded: %d\n", + num_bams); + return SPS_ERROR; + } + } else { + SPS_ERR(sps, + "sps:%s:BAM-DMA phys_addr is zero\n", + __func__); + return SPS_ERROR; + } + + + return 0; +} + +/** + * De-initialize BAM DMA module + * + */ +void sps_dma_de_init(void) +{ + int n; + + /* De-initialize the BAM devices */ + for (n = 0; n < num_bams; n++) + sps_deregister_bam_device(bam_handles[n]); + + /* Clear local data */ + memset(&bam_dma_dev, 0, sizeof(bam_dma_dev)); + num_bams = 0; + memset(bam_handles, 0, sizeof(bam_handles)); +} + +/** + * Allocate a BAM DMA channel + * + */ +int sps_alloc_dma_chan(const struct sps_alloc_dma_chan *alloc, + struct sps_dma_chan *chan_info) +{ + struct bamdma_device *dev; + struct bamdma_chan *chan; + u32 pipe_index; + enum bam_dma_thresh_dma thresh = (enum bam_dma_thresh_dma) 0; + enum bam_dma_weight_dma weight = (enum bam_dma_weight_dma) 0; + int result = SPS_ERROR; + + if (alloc == NULL || chan_info == NULL) { + SPS_ERR(sps, + "sps:%s:invalid parameters\n", __func__); + return SPS_ERROR; + } + + /* Translate threshold and priority to hwio values */ + if (alloc->threshold != SPS_DMA_THRESHOLD_DEFAULT) { + if (alloc->threshold >= 512) + thresh = BAM_DMA_THRESH_512; + else if (alloc->threshold >= 256) + thresh = BAM_DMA_THRESH_256; + else if (alloc->threshold >= 128) + thresh = BAM_DMA_THRESH_128; + else + thresh = BAM_DMA_THRESH_64; + } + + weight = alloc->priority; + + if ((u32)alloc->priority > (u32)BAM_DMA_WEIGHT_HIGH) { + SPS_ERR(sps, "sps:BAM-DMA: invalid priority: %x\n", + alloc->priority); + return SPS_ERROR; + } + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device(alloc->dev); + if (dev == NULL) { + SPS_ERR(sps, "sps:BAM-DMA: invalid BAM handle: %pK\n", + (void *)alloc->dev); + goto exit_err; + } + + /* Search for a free set of pipes */ + for (pipe_index = 0, chan = dev->chans; + pipe_index < dev->num_pipes; pipe_index += 2, chan++) { + if (chan->state == DMA_CHAN_STATE_FREE) { + /* Just check pipes for safety */ + if (dev->pipes[pipe_index] != PIPE_INACTIVE || + dev->pipes[pipe_index + 1] != PIPE_INACTIVE) { + SPS_ERR(sps, + "sps:BAM-DMA: channel %d state error:%d %d\n", + pipe_index / 2, dev->pipes[pipe_index], + dev->pipes[pipe_index + 1]); + goto exit_err; + } + break; /* Found free pipe */ + } + } + + if (pipe_index >= dev->num_pipes) { + SPS_ERR(sps, "sps:BAM-DMA: no free channel num_pipes = %d\n", + dev->num_pipes); + goto exit_err; + } + + chan->state = DMA_CHAN_STATE_ALLOC_EXT; + + /* Store config values for use when pipes are activated */ + chan = &dev->chans[pipe_index / 2]; + chan->threshold = alloc->threshold; + chan->thresh = thresh; + chan->priority = alloc->priority; + chan->weight = weight; + + SPS_DBG3(sps, "sps:%s. pipe %d\n", __func__, pipe_index); + + /* Report allocated pipes to client */ + chan_info->dev = dev->h; + /* Dest/input/write pipex */ + chan_info->dest_pipe_index = pipe_index; + /* Source/output/read pipe */ + chan_info->src_pipe_index = pipe_index + 1; + + result = 0; +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} +EXPORT_SYMBOL(sps_alloc_dma_chan); + +/** + * Free a BAM DMA channel + * + */ +int sps_free_dma_chan(struct sps_dma_chan *chan) +{ + struct bamdma_device *dev; + u32 pipe_index; + int result = 0; + + if (chan == NULL) { + SPS_ERR(sps, + "sps:%s:chan is NULL\n", __func__); + return SPS_ERROR; + } + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device(chan->dev); + if (dev == NULL) { + SPS_ERR(sps, "sps:BAM-DMA: invalid BAM handle: %pK\n", + (void *)chan->dev); + result = SPS_ERROR; + goto exit_err; + } + + /* Verify the pipe indices */ + pipe_index = chan->dest_pipe_index; + if (pipe_index >= dev->num_pipes || ((pipe_index & 1)) || + (pipe_index + 1) != chan->src_pipe_index) { + SPS_ERR(sps, + "sps:%s. Invalid pipe indices num_pipes=%d dest=%d src=%d\n", + __func__, dev->num_pipes, + chan->dest_pipe_index, + chan->src_pipe_index); + result = SPS_ERROR; + goto exit_err; + } + + /* Are both pipes inactive? */ + if (dev->chans[pipe_index / 2].state != DMA_CHAN_STATE_ALLOC_EXT || + dev->pipes[pipe_index] != PIPE_INACTIVE || + dev->pipes[pipe_index + 1] != PIPE_INACTIVE) { + SPS_ERR(sps, + "sps:BAM-DMA: attempt to free active chan %d: %d %d\n", + pipe_index / 2, dev->pipes[pipe_index], + dev->pipes[pipe_index + 1]); + result = SPS_ERROR; + goto exit_err; + } + + /* Free the channel */ + dev->chans[pipe_index / 2].state = DMA_CHAN_STATE_FREE; + +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} +EXPORT_SYMBOL(sps_free_dma_chan); + +/** + * Activate a BAM DMA pipe + * + * This function activates a BAM DMA pipe. + * + * @dev - pointer to BAM-DMA device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +static u32 sps_dma_check_pipes(struct bamdma_device *dev, u32 pipe_index) +{ + u32 pipe_in; + u32 pipe_out; + int enabled_in; + int enabled_out; + u32 check; + + pipe_in = pipe_index & ~1; + pipe_out = pipe_in + 1; + enabled_in = bam_pipe_is_enabled(&dev->bam->base, pipe_in); + enabled_out = bam_pipe_is_enabled(&dev->bam->base, pipe_out); + + if (!enabled_in && !enabled_out) + check = DMA_PIPES_BOTH_DISABLED; + else if (enabled_in && enabled_out) + check = DMA_PIPES_BOTH_ENABLED; + else + check = DMA_PIPES_STATE_DIFF; + + return check; +} + +/** + * Allocate a BAM DMA pipe + * + */ +int sps_dma_pipe_alloc(void *bam_arg, u32 pipe_index, enum sps_mode dir) +{ + struct sps_bam *bam = bam_arg; + struct bamdma_device *dev; + struct bamdma_chan *chan; + u32 channel; + int result = SPS_ERROR; + + if (bam == NULL) { + SPS_ERR(sps, "%s\n", "sps:BAM context is NULL"); + return SPS_ERROR; + } + + /* Check pipe direction */ + if ((DMA_PIPE_IS_DEST(pipe_index) && dir != SPS_MODE_DEST) || + (DMA_PIPE_IS_SRC(pipe_index) && dir != SPS_MODE_SRC)) { + SPS_ERR(sps, "sps:BAM-DMA: wrong dir for BAM %pa pipe %d\n", + &bam->props.phys_addr, pipe_index); + return SPS_ERROR; + } + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device((unsigned long) bam); + if (dev == NULL) { + SPS_ERR(sps, "sps:BAM-DMA: invalid BAM: %pa\n", + &bam->props.phys_addr); + goto exit_err; + } + if (pipe_index >= dev->num_pipes) { + SPS_ERR(sps, "sps:BAM-DMA: BAM %pa invalid pipe: %d\n", + &bam->props.phys_addr, pipe_index); + goto exit_err; + } + if (dev->pipes[pipe_index] != PIPE_INACTIVE) { + SPS_ERR(sps, "sps:BAM-DMA: BAM %pa pipe %d already active\n", + &bam->props.phys_addr, pipe_index); + goto exit_err; + } + + /* Mark pipe active */ + dev->pipes[pipe_index] = PIPE_ACTIVE; + + /* If channel is not allocated, make an internal allocation */ + channel = pipe_index / 2; + chan = &dev->chans[channel]; + if (chan->state != DMA_CHAN_STATE_ALLOC_EXT && + chan->state != DMA_CHAN_STATE_ALLOC_INT) { + chan->state = DMA_CHAN_STATE_ALLOC_INT; + } + + result = 0; +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * Enable a BAM DMA pipe + * + */ +int sps_dma_pipe_enable(void *bam_arg, u32 pipe_index) +{ + struct sps_bam *bam = bam_arg; + struct bamdma_device *dev; + struct bamdma_chan *chan; + u32 channel; + int result = SPS_ERROR; + + SPS_DBG3(sps, "sps:%s pipe %d\n", __func__, pipe_index); + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device((unsigned long) bam); + if (dev == NULL) { + SPS_ERR(sps, "sps:%s:BAM-DMA: invalid BAM\n", __func__); + goto exit_err; + } + if (pipe_index >= dev->num_pipes) { + SPS_ERR(sps, "sps:BAM-DMA: BAM %pa invalid pipe: %d\n", + &bam->props.phys_addr, pipe_index); + goto exit_err; + } + if (dev->pipes[pipe_index] != PIPE_ACTIVE) { + SPS_ERR(sps, "sps:BAM-DMA: BAM %pa pipe %d not active\n", + &bam->props.phys_addr, pipe_index); + goto exit_err; + } + + /* + * The channel must be enabled when the dest/input/write pipe + * is enabled + */ + if (DMA_PIPE_IS_DEST(pipe_index)) { + /* Configure and enable the channel */ + channel = pipe_index / 2; + chan = &dev->chans[channel]; + + if (chan->threshold != SPS_DMA_THRESHOLD_DEFAULT) + dma_write_reg_field(dev->virt_addr, + DMA_CHNL_CONFIG(channel), + DMA_CHNL_ACT_THRESH, + chan->thresh); + + if (chan->priority != SPS_DMA_PRI_DEFAULT) + dma_write_reg_field(dev->virt_addr, + DMA_CHNL_CONFIG(channel), + DMA_CHNL_WEIGHT, + chan->weight); + + dma_write_reg_field(dev->virt_addr, + DMA_CHNL_CONFIG(channel), + DMA_CHNL_ENABLE, 1); + } + + result = 0; +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * Deactivate a BAM DMA pipe + * + * This function deactivates a BAM DMA pipe. + * + * @dev - pointer to BAM-DMA device descriptor + * + * @bam - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +static int sps_dma_deactivate_pipe_atomic(struct bamdma_device *dev, + struct sps_bam *bam, + u32 pipe_index) +{ + u32 channel; + + if (dev->bam != bam) + return SPS_ERROR; + if (pipe_index >= dev->num_pipes) + return SPS_ERROR; + if (dev->pipes[pipe_index] != PIPE_ACTIVE) + return SPS_ERROR; /* Pipe is not active */ + + SPS_DBG3(sps, "sps:BAM-DMA: deactivate pipe %d\n", pipe_index); + + /* Mark pipe inactive */ + dev->pipes[pipe_index] = PIPE_INACTIVE; + + /* + * Channel must be reset when either pipe is disabled, so just always + * reset regardless of other pipe's state + */ + channel = pipe_index / 2; + dma_write_reg_field(dev->virt_addr, DMA_CHNL_CONFIG(channel), + DMA_CHNL_ENABLE, 0); + + /* If the peer pipe is also inactive, reset the channel */ + if (sps_dma_check_pipes(dev, pipe_index) == DMA_PIPES_BOTH_DISABLED) { + /* Free channel if allocated internally */ + if (dev->chans[channel].state == DMA_CHAN_STATE_ALLOC_INT) + dev->chans[channel].state = DMA_CHAN_STATE_FREE; + } + + return 0; +} + +/** + * Free a BAM DMA pipe + * + */ +int sps_dma_pipe_free(void *bam_arg, u32 pipe_index) +{ + struct bamdma_device *dev; + struct sps_bam *bam = bam_arg; + int result; + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device((unsigned long) bam); + if (dev == NULL) { + SPS_ERR(sps, "sps:%s:BAM-DMA: invalid BAM\n", __func__); + result = SPS_ERROR; + goto exit_err; + } + + result = sps_dma_deactivate_pipe_atomic(dev, bam, pipe_index); + +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * Get the BAM handle for BAM-DMA. + * + * The BAM handle should be use as source/destination in the sps_connect(). + * + * @return bam handle on success, zero on error + */ +unsigned long sps_dma_get_bam_handle(void) +{ + return (unsigned long)bam_dma_dev[0].bam; +} +EXPORT_SYMBOL(sps_dma_get_bam_handle); + +/** + * Free the BAM handle for BAM-DMA. + * + */ +void sps_dma_free_bam_handle(unsigned long h) +{ +} +EXPORT_SYMBOL(sps_dma_free_bam_handle); + +#endif /* CONFIG_SPS_SUPPORT_BAMDMA */ diff --git a/drivers/soc/qcom/sps/sps_map.c b/drivers/soc/qcom/sps/sps_map.c new file mode 100644 index 000000000000..d046d55dcc38 --- /dev/null +++ b/drivers/soc/qcom/sps/sps_map.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011-2013, 2015, 2017-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/** + * Connection mapping table management for SPS device driver. + */ + +#include +#include +#include + +#include "spsi.h" + +/* Module state */ +struct sps_map_state { + const struct sps_map *maps; + u32 num_maps; + u32 options; +}; + +static struct sps_map_state sps_maps; + +/** + * Initialize connection mapping module + * + */ +int sps_map_init(const struct sps_map *map_props, u32 options) +{ + const struct sps_map *maps; + + /* Are there any connection mappings? */ + memset(&sps_maps, 0, sizeof(sps_maps)); + if (map_props == NULL) + return 0; + + /* Init the module state */ + sps_maps.maps = map_props; + sps_maps.options = options; + for (maps = sps_maps.maps;; maps++, sps_maps.num_maps++) + if (maps->src.periph_class == SPS_CLASS_INVALID && + maps->src.periph_phy_addr == SPS_ADDR_INVALID) + break; + + SPS_DBG(sps, "sps: %d mappings\n", sps_maps.num_maps); + + return 0; +} + +/** + * De-initialize connection mapping module + * + */ +void sps_map_de_init(void) +{ + memset(&sps_maps, 0, sizeof(sps_maps)); +} + +/** + * Find matching connection mapping + * + */ +int sps_map_find(struct sps_connect *connect) +{ + const struct sps_map *map; + u32 i; + void *desc; + void *data; + + /* Are there any connection mappings? */ + if (sps_maps.num_maps == 0) + return SPS_ERROR; + + /* Search the mapping table for a match to the specified connection */ + for (i = sps_maps.num_maps, map = sps_maps.maps; + i > 0; i--, map++) + if (map->src.periph_class == (u32) connect->source && + map->dest.periph_class == (u32) connect->destination + && map->config == (u32) connect->config) + break; + + if (i == 0) + return SPS_ERROR; + + /* + * Before modifying client parameter struct, perform all + * operations that might fail + */ + desc = spsi_get_mem_ptr(map->desc_base); + if (desc == NULL) { + SPS_ERR(sps, + "sps:Cannot get virt addr for I/O buffer: %pa\n", + &map->desc_base); + return SPS_ERROR; + } + + if (map->data_size > 0 && map->data_base != SPS_ADDR_INVALID) { + data = spsi_get_mem_ptr(map->data_base); + if (data == NULL) { + SPS_ERR(sps, + "sps:Can't get virt addr for I/O buffer: %pa\n", + &map->data_base); + return SPS_ERROR; + } + } else { + data = NULL; + } + + /* Copy mapping values to client parameter struct */ + if (connect->source != SPS_DEV_HANDLE_MEM) + connect->src_pipe_index = map->src.pipe_index; + + if (connect->destination != SPS_DEV_HANDLE_MEM) + connect->dest_pipe_index = map->dest.pipe_index; + + if (connect->mode == SPS_MODE_SRC) + connect->event_thresh = map->src.event_thresh; + else + connect->event_thresh = map->dest.event_thresh; + + connect->desc.size = map->desc_size; + connect->desc.phys_base = map->desc_base; + connect->desc.base = desc; + if (map->data_size > 0 && map->data_base != SPS_ADDR_INVALID) { + connect->data.size = map->data_size; + connect->data.phys_base = map->data_base; + connect->data.base = data; + } + + return 0; +} diff --git a/drivers/soc/qcom/sps/sps_map.h b/drivers/soc/qcom/sps/sps_map.h new file mode 100644 index 000000000000..1d6e8ff4a483 --- /dev/null +++ b/drivers/soc/qcom/sps/sps_map.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011, 2013, 2016-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/* SPS driver mapping table data declarations. */ + + +#ifndef _SPS_MAP_H_ +#define _SPS_MAP_H_ + +#include + +/* End point parameters */ +struct sps_map_end_point { + u32 periph_class; /* Peripheral device enumeration class */ + phys_addr_t periph_phy_addr; /* Peripheral base address */ + u32 pipe_index; /* Pipe index */ + u32 event_thresh; /* Pipe event threshold */ +}; + +/* Mapping connection descriptor */ +struct sps_map { + /* Source end point parameters */ + struct sps_map_end_point src; + + /* Destination end point parameters */ + struct sps_map_end_point dest; + + /* Resource parameters */ + u32 config; /* Configuration (stream) identifier */ + phys_addr_t desc_base; /* Physical address of descriptor FIFO */ + u32 desc_size; /* Size (bytes) of descriptor FIFO */ + phys_addr_t data_base; /* Physical address of data FIFO */ + u32 data_size; /* Size (bytes) of data FIFO */ + +}; + +#endif /* _SPS_MAP_H_ */ diff --git a/drivers/soc/qcom/sps/sps_mem.c b/drivers/soc/qcom/sps/sps_mem.c new file mode 100644 index 000000000000..63a5e268bda7 --- /dev/null +++ b/drivers/soc/qcom/sps/sps_mem.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011, 2013, 2015, 2017-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/** + * Pipe-Memory allocation/free management. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sps_bam.h" +#include "spsi.h" + +static phys_addr_t iomem_phys; +static void *iomem_virt; +static u32 iomem_size; +static u32 iomem_offset; +static struct gen_pool *pool; +static u32 nid = 0xaa; + +/* Debug */ +static u32 total_alloc; +static u32 total_free; + +/** + * Translate physical to virtual address + * + */ +void *spsi_get_mem_ptr(phys_addr_t phys_addr) +{ + void *virt = NULL; + + if ((phys_addr >= iomem_phys) && + (phys_addr < (iomem_phys + iomem_size))) { + virt = (u8 *) iomem_virt + (phys_addr - iomem_phys); + } else { + virt = phys_to_virt(phys_addr); + SPS_ERR(sps, "sps:%s.invalid phys addr=0x%pa\n", + __func__, &phys_addr); + } + return virt; +} + +/** + * Allocate I/O (pipe) memory + * + */ +phys_addr_t sps_mem_alloc_io(u32 bytes) +{ + phys_addr_t phys_addr = SPS_ADDR_INVALID; + unsigned long virt_addr = 0; + + virt_addr = gen_pool_alloc(pool, bytes); + if (virt_addr) { + iomem_offset = virt_addr - (uintptr_t) iomem_virt; + phys_addr = iomem_phys + iomem_offset; + total_alloc += bytes; + } else { + SPS_ERR(sps, "sps:gen_pool_alloc %d bytes fail\n", bytes); + return SPS_ADDR_INVALID; + } + + SPS_DBG3(sps, "sps:%s.phys=%pa.virt=0x%pK.size=0x%x\n", + __func__, &phys_addr, (void *)virt_addr, bytes); + + return phys_addr; +} + +/** + * Free I/O memory + * + */ +void sps_mem_free_io(phys_addr_t phys_addr, u32 bytes) +{ + unsigned long virt_addr = 0; + + iomem_offset = phys_addr - iomem_phys; + virt_addr = (uintptr_t) iomem_virt + iomem_offset; + + SPS_DBG3(sps, "sps:%s.phys=%pa.virt=0x%pK.size=0x%x\n", + __func__, &phys_addr, (void *)virt_addr, bytes); + + gen_pool_free(pool, virt_addr, bytes); + total_free += bytes; +} + +/** + * Initialize driver memory module + * + */ +int sps_mem_init(phys_addr_t pipemem_phys_base, u32 pipemem_size) +{ + int res; + + /* 2^8=128. The desc-fifo and data-fifo minimal allocation. */ + int min_alloc_order = 8; + + if ((d_type == 0) || (d_type == 2) || imem) { + iomem_phys = pipemem_phys_base; + iomem_size = pipemem_size; + + if (iomem_phys == 0) { + SPS_ERR(sps, "sps:%s:Invalid Pipe-Mem address\n", + __func__); + return SPS_ERROR; + } + iomem_virt = ioremap(iomem_phys, iomem_size); + if (!iomem_virt) { + SPS_ERR(sps, + "sps:%s:Failed to IO map pipe memory\n", + __func__); + return -ENOMEM; + } + + iomem_offset = 0; + SPS_DBG(sps, + "sps:%s.iomem_phys=%pa,iomem_virt=0x%pK\n", + __func__, &iomem_phys, iomem_virt); + } + + pool = gen_pool_create(min_alloc_order, nid); + + if (!pool) { + SPS_ERR(sps, "sps:%s:Failed to create a new memory pool\n", + __func__); + return -ENOMEM; + } + + if ((d_type == 0) || (d_type == 2) || imem) { + res = gen_pool_add(pool, (uintptr_t)iomem_virt, + iomem_size, nid); + if (res) + return res; + } + + return 0; +} + +/** + * De-initialize driver memory module + * + */ +int sps_mem_de_init(void) +{ + if (iomem_virt != NULL) { + gen_pool_destroy(pool); + pool = NULL; + iounmap(iomem_virt); + iomem_virt = NULL; + } + + if (total_alloc == total_free) + return 0; + + SPS_ERR(sps, "sps:%s:some memory not free\n", __func__); + return SPS_ERROR; +} diff --git a/drivers/soc/qcom/sps/sps_rm.c b/drivers/soc/qcom/sps/sps_rm.c new file mode 100644 index 000000000000..e6631006e9f3 --- /dev/null +++ b/drivers/soc/qcom/sps/sps_rm.c @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011-2015, 2017-2019, 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/* Resource management for the SPS device driver. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "spsi.h" +#include "sps_core.h" + +/* Max BAM FIFO sizes */ +#define SPSRM_MAX_DESC_FIFO_SIZE 0xffff +#define SPSRM_MAX_DATA_FIFO_SIZE 0xffff + +/* Connection control struct pointer */ +static struct sps_rm *sps_rm; + +/** + * Initialize resource manager module + */ +int sps_rm_init(struct sps_rm *rm, u32 options) +{ + /* Set the resource manager state struct pointer */ + sps_rm = rm; + + /* Initialize the state struct */ + INIT_LIST_HEAD(&sps_rm->connections_q); + mutex_init(&sps_rm->lock); + + return 0; +} + +/** + * Initialize client state context + * + */ +void sps_rm_config_init(struct sps_connect *connect) +{ + memset(connect, SPSRM_CLEAR, sizeof(*connect)); +} + +/** + * Remove reference to connection mapping + * + * This function removes a reference from a connection mapping struct. + * + * @map - pointer to connection mapping struct + * + */ +static void sps_rm_remove_ref(struct sps_connection *map) +{ + /* Free this connection */ + map->refs--; + if (map->refs <= 0) { + if (map->client_src != NULL || map->client_dest != NULL) + SPS_ERR(sps, + "sps:%s:Failed to allocate connection struct\n", + __func__); + + list_del(&map->list); + kfree(map); + } +} + +/** + * Compare map to connect parameters + * + * This function compares client connect parameters to an allocated + * connection mapping. + * + * @pipe - client context for SPS connection end point + * + * @return - true if match, false otherwise + * + */ +static int sps_rm_map_match(const struct sps_connect *cfg, + const struct sps_connection *map) +{ + if (cfg->source != map->src.dev || + cfg->destination != map->dest.dev) + return false; + + if (cfg->src_pipe_index != SPSRM_CLEAR && + cfg->src_pipe_index != map->src.pipe_index) + return false; + + if (cfg->dest_pipe_index != SPSRM_CLEAR && + cfg->dest_pipe_index != map->dest.pipe_index) + return false; + + if (cfg->config != map->config) + return false; + + if (cfg->desc.size != SPSRM_CLEAR) { + if (cfg->desc.size != map->desc.size) + return false; + + if (cfg->desc.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) && + cfg->desc.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR) && + (cfg->desc.phys_base != map->desc.phys_base || + cfg->desc.base != map->desc.base)) { + return false; + } + } + + if (cfg->data.size != SPSRM_CLEAR) { + if (cfg->data.size != map->data.size) + return false; + + if (cfg->data.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) && + cfg->data.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR) && + (cfg->data.phys_base != map->data.phys_base || + cfg->data.base != map->data.base)) + return false; + } + + return true; +} + +/** + * Find unconnected mapping + * + * This function finds an allocated a connection mapping. + * + * @pipe - client context for SPS connection end point + * + * @return - pointer to allocated connection mapping, or NULL if not found + * + */ +static struct sps_connection *find_unconnected(struct sps_pipe *pipe) +{ + struct sps_connect *cfg = &pipe->connect; + struct sps_connection *map; + + /* Has this connection already been allocated? */ + list_for_each_entry(map, &sps_rm->connections_q, list) { + if (sps_rm_map_match(cfg, map)) + if ((cfg->mode == SPS_MODE_SRC + && map->client_src == NULL) + || (cfg->mode != SPS_MODE_SRC + && map->client_dest == NULL)) + return map; /* Found */ + } + + return NULL; /* Not Found */ +} + +/** + * Assign connection to client + * + * This function assigns a connection to a client. + * + * @pipe - client context for SPS connection end point + * + * @map - connection mapping + * + * @return 0 on success, negative value on error + * + */ +static int sps_rm_assign(struct sps_pipe *pipe, + struct sps_connection *map) +{ + struct sps_connect *cfg = &pipe->connect; + unsigned long desc_iova = 0; + unsigned long data_iova = 0; + + /* Check ownership and BAM */ + if ((cfg->mode == SPS_MODE_SRC && map->client_src != NULL) || + (cfg->mode != SPS_MODE_SRC && map->client_dest != NULL)) { + SPS_ERR(sps, + "sps:%s:The end point is already connected\n", + __func__); + return SPS_ERROR; + } + + /* Check whether this end point is a BAM (not memory) */ + if ((cfg->mode == SPS_MODE_SRC && map->src.bam == NULL) || + (cfg->mode != SPS_MODE_SRC && map->dest.bam == NULL)) { + SPS_ERR(sps, "sps:%s:The end point is empty\n", __func__); + return SPS_ERROR; + } + + /* Record the connection assignment */ + if (cfg->mode == SPS_MODE_SRC) { + map->client_src = pipe; + pipe->bam = map->src.bam; + pipe->pipe_index = map->src.pipe_index; + if (pipe->connect.event_thresh != SPSRM_CLEAR) + map->src.event_threshold = pipe->connect.event_thresh; + if (pipe->connect.lock_group != SPSRM_CLEAR) + map->src.lock_group = pipe->connect.lock_group; + } else { + map->client_dest = pipe; + pipe->bam = map->dest.bam; + pipe->pipe_index = map->dest.pipe_index; + if (pipe->connect.event_thresh != SPSRM_CLEAR) + map->dest.event_threshold = + pipe->connect.event_thresh; + if (pipe->connect.lock_group != SPSRM_CLEAR) + map->dest.lock_group = pipe->connect.lock_group; + } + pipe->map = map; + + SPS_DBG(pipe->bam, "sps:%s.bam %pa.pipe_index=%d\n", + __func__, BAM_ID(pipe->bam), pipe->pipe_index); + + /* Copy parameters to client connect state */ + pipe->connect.src_pipe_index = map->src.pipe_index; + pipe->connect.dest_pipe_index = map->dest.pipe_index; + + /* + * The below assignment to connect.desc and connect.data will + * overwrite the previous values given by the first client + * in a BAM-to-BAM connection. Prevent that since the IOVAs + * may be different for the same physical buffers if the + * BAMs use different SMMUs. + */ + if (pipe->bam->props.options & SPS_BAM_SMMU_EN) { + desc_iova = pipe->connect.desc.iova; + data_iova = pipe->connect.data.iova; + } + pipe->connect.desc = map->desc; + pipe->connect.data = map->data; + if (pipe->bam->props.options & SPS_BAM_SMMU_EN) { + pipe->connect.desc.iova = desc_iova; + pipe->connect.data.iova = data_iova; + } + + pipe->client_state = SPS_STATE_ALLOCATE; + + return 0; +} + +/** + * Free connection mapping resources + * + * This function frees a connection mapping resources. + * + * @pipe - client context for SPS connection end point + * + */ +static void sps_rm_free_map_rsrc(struct sps_connection *map) +{ + struct sps_bam *bam; + + if (map->client_src != NULL || map->client_dest != NULL) + return; + + if (map->alloc_src_pipe != SPS_BAM_PIPE_INVALID) { + bam = map->src.bam; + sps_bam_pipe_free(bam, map->src.pipe_index); + + /* Is this a BAM-DMA pipe? */ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) + /* Deallocate and free the BAM-DMA channel */ + sps_dma_pipe_free(bam, map->src.pipe_index); +#endif + map->alloc_src_pipe = SPS_BAM_PIPE_INVALID; + map->src.pipe_index = SPS_BAM_PIPE_INVALID; + } + if (map->alloc_dest_pipe != SPS_BAM_PIPE_INVALID) { + bam = map->dest.bam; + sps_bam_pipe_free(bam, map->dest.pipe_index); + + /* Is this a BAM-DMA pipe? */ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) { + /* Deallocate the BAM-DMA channel */ + sps_dma_pipe_free(bam, map->dest.pipe_index); + } +#endif + map->alloc_dest_pipe = SPS_BAM_PIPE_INVALID; + map->dest.pipe_index = SPS_BAM_PIPE_INVALID; + } + if (map->alloc_desc_base != SPS_ADDR_INVALID) { + sps_mem_free_io(map->alloc_desc_base, map->desc.size); + + map->alloc_desc_base = SPS_ADDR_INVALID; + map->desc.phys_base = SPS_ADDR_INVALID; + } + if (map->alloc_data_base != SPS_ADDR_INVALID) { + sps_mem_free_io(map->alloc_data_base, map->data.size); + + map->alloc_data_base = SPS_ADDR_INVALID; + map->data.phys_base = SPS_ADDR_INVALID; + } +} + +/** + * Init connection mapping from client connect + * + * This function initializes a connection mapping from the client's + * connect parameters. + * + * @map - connection mapping struct + * + * @cfg - client connect parameters + * + * @return - pointer to allocated connection mapping, or NULL on error + * + */ +static void sps_rm_init_map(struct sps_connection *map, + const struct sps_connect *cfg) +{ + /* Clear the connection mapping struct */ + memset(map, 0, sizeof(*map)); + map->desc.phys_base = SPS_ADDR_INVALID; + map->data.phys_base = SPS_ADDR_INVALID; + map->alloc_desc_base = SPS_ADDR_INVALID; + map->alloc_data_base = SPS_ADDR_INVALID; + map->alloc_src_pipe = SPS_BAM_PIPE_INVALID; + map->alloc_dest_pipe = SPS_BAM_PIPE_INVALID; + + /* Copy client required parameters */ + map->src.dev = cfg->source; + map->dest.dev = cfg->destination; + map->desc.size = cfg->desc.size; + map->data.size = cfg->data.size; + map->config = cfg->config; + + /* Did client specify descriptor FIFO? */ + if (map->desc.size != SPSRM_CLEAR && + cfg->desc.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) && + cfg->desc.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR)) + map->desc = cfg->desc; + + /* Did client specify data FIFO? */ + if (map->data.size != SPSRM_CLEAR && + cfg->data.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) && + cfg->data.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR)) + map->data = cfg->data; + + /* Did client specify source pipe? */ + if (cfg->src_pipe_index != SPSRM_CLEAR) + map->src.pipe_index = cfg->src_pipe_index; + else + map->src.pipe_index = SPS_BAM_PIPE_INVALID; + + + /* Did client specify destination pipe? */ + if (cfg->dest_pipe_index != SPSRM_CLEAR) + map->dest.pipe_index = cfg->dest_pipe_index; + else + map->dest.pipe_index = SPS_BAM_PIPE_INVALID; +} + +/** + * Create a new connection mapping + * + * This function creates a new connection mapping. + * + * @pipe - client context for SPS connection end point + * + * @return - pointer to allocated connection mapping, or NULL on error + * + */ +static struct sps_connection *sps_rm_create(struct sps_pipe *pipe) +{ + struct sps_connection *map; + struct sps_bam *bam; + u32 desc_size; + u32 data_size; + enum sps_mode dir; + int success = false; + + /* Allocate new connection */ + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (map == NULL) { + SPS_ERR(sps, + "sps:%s:Failed to allocate connection struct\n", + __func__); + return NULL; + } + + /* Initialize connection struct */ + sps_rm_init_map(map, &pipe->connect); + dir = pipe->connect.mode; + + /* Use a do/while() loop to avoid a "goto" */ + success = false; + /* Get BAMs */ + map->src.bam = sps_h2bam(map->src.dev); + if (map->src.bam == NULL) { + if (map->src.dev != SPS_DEV_HANDLE_MEM) { + SPS_ERR(sps, "sps:Invalid BAM handle: %pK\n", + (void *)(&map->src.dev)); + goto exit_err; + } + map->src.pipe_index = SPS_BAM_PIPE_INVALID; + } + + if (!(pipe->connect.options & SPS_O_DUMMY_PEER)) { + map->dest.bam = sps_h2bam(map->dest.dev); + if (map->dest.bam == NULL) { + if (map->dest.dev != SPS_DEV_HANDLE_MEM) { + SPS_ERR(sps, + "sps:Invalid BAM handle: %pK", + (void *)(&map->dest.dev)); + goto exit_err; + } + map->dest.pipe_index = SPS_BAM_PIPE_INVALID; + } + } + + /* Check the BAM device for the pipe */ + if ((dir == SPS_MODE_SRC && map->src.bam == NULL) || + (dir != SPS_MODE_SRC && map->dest.bam == NULL)) { + SPS_ERR(sps, "sps:Invalid BAM endpt: dir %d src %pK dest %pK\n", + dir, (void *)(&map->src.dev), (void *)(&map->dest.dev)); + goto exit_err; + } + + /* Allocate pipes and copy BAM parameters */ + if (map->src.bam != NULL) { + /* Allocate the pipe */ + bam = map->src.bam; + map->alloc_src_pipe = sps_bam_pipe_alloc(bam, + map->src.pipe_index); + if (map->alloc_src_pipe == SPS_BAM_PIPE_INVALID) + goto exit_err; + map->src.pipe_index = map->alloc_src_pipe; + + /* Is this a BAM-DMA pipe? */ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) { + int rc; + /* Allocate the BAM-DMA channel */ + rc = sps_dma_pipe_alloc(bam, map->src.pipe_index, + SPS_MODE_SRC); + if (rc) { + SPS_ERR(bam, + "sps:Failed to alloc BAM-DMA pipe: %d\n", + map->src.pipe_index); + goto exit_err; + } + } +#endif + map->src.bam_phys = bam->props.phys_addr; + map->src.event_threshold = bam->props.event_threshold; + } + if (map->dest.bam != NULL) { + /* Allocate the pipe */ + bam = map->dest.bam; + map->alloc_dest_pipe = sps_bam_pipe_alloc(bam, + map->dest.pipe_index); + if (map->alloc_dest_pipe == SPS_BAM_PIPE_INVALID) + goto exit_err; + + map->dest.pipe_index = map->alloc_dest_pipe; + + /* Is this a BAM-DMA pipe? */ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) { + int rc; + /* Allocate the BAM-DMA channel */ + rc = sps_dma_pipe_alloc(bam, map->dest.pipe_index, + SPS_MODE_DEST); + if (rc) { + SPS_ERR(bam, + "sps:Failed to alloc BAM-DMA pipe: %d\n", + map->dest.pipe_index); + goto exit_err; + } + } +#endif + map->dest.bam_phys = bam->props.phys_addr; + map->dest.event_threshold = + bam->props.event_threshold; + } + + /* Get default FIFO sizes */ + desc_size = 0; + data_size = 0; + if (map->src.bam != NULL) { + bam = map->src.bam; + desc_size = bam->props.desc_size; + data_size = bam->props.data_size; + } + if (map->dest.bam != NULL) { + bam = map->dest.bam; + if (bam->props.desc_size > desc_size) + desc_size = bam->props.desc_size; + if (bam->props.data_size > data_size) + data_size = bam->props.data_size; + } + + /* Set FIFO sizes */ + if (map->desc.size == SPSRM_CLEAR) + map->desc.size = desc_size; + if (map->src.bam != NULL && map->dest.bam != NULL) { + /* BAM-to-BAM requires data FIFO */ + if (map->data.size == SPSRM_CLEAR) + map->data.size = data_size; + } else { + if (!(pipe->connect.options & SPS_O_DUMMY_PEER)) + map->data.size = 0; + } + if (map->desc.size > SPSRM_MAX_DESC_FIFO_SIZE) { + SPS_ERR(sps, "sps:Invalid desc FIFO size: 0x%x\n", + map->desc.size); + goto exit_err; + } + if (map->src.bam != NULL && map->dest.bam != NULL && + map->data.size > SPSRM_MAX_DATA_FIFO_SIZE) { + SPS_ERR(sps, "sps:Invalid data FIFO size: 0x%x\n", + map->data.size); + goto exit_err; + } + + /* Allocate descriptor FIFO if necessary */ + if (map->desc.size && map->desc.phys_base == SPS_ADDR_INVALID) { + map->alloc_desc_base = sps_mem_alloc_io(map->desc.size); + if (map->alloc_desc_base == SPS_ADDR_INVALID) { + SPS_ERR(sps, "sps:I/O memory allocation failure:0x%x\n", + map->desc.size); + goto exit_err; + } + map->desc.phys_base = map->alloc_desc_base; + map->desc.base = spsi_get_mem_ptr(map->desc.phys_base); + if (map->desc.base == NULL) { + SPS_ERR(sps, + "sps:Cannot get virt addr for I/O buffer:%pa\n", + &map->desc.phys_base); + goto exit_err; + } + } + + /* Allocate data FIFO if necessary */ + if (map->data.size && map->data.phys_base == SPS_ADDR_INVALID) { + map->alloc_data_base = sps_mem_alloc_io(map->data.size); + if (map->alloc_data_base == SPS_ADDR_INVALID) { + SPS_ERR(sps, "sps:I/O memory allocation failure:0x%x\n", + map->data.size); + goto exit_err; + } + map->data.phys_base = map->alloc_data_base; + map->data.base = spsi_get_mem_ptr(map->data.phys_base); + if (map->data.base == NULL) { + SPS_ERR(sps, + "sps:Cannot get virt addr for I/O buffer:%pa\n", + &map->data.phys_base); + goto exit_err; + } + } + + /* Attempt to assign this connection to the client */ + if (sps_rm_assign(pipe, map)) { + SPS_ERR(sps, + "sps:%s:failed to assign a connection to the client\n", + __func__); + goto exit_err; + } + + /* Initialization was successful */ + success = true; +exit_err: + + /* If initialization failed, free resources */ + if (!success) { + sps_rm_free_map_rsrc(map); + kfree(map); + return NULL; + } + + return map; +} + +/** + * Free connection mapping + * + * This function frees a connection mapping. + * + * @pipe - client context for SPS connection end point + * + * @return 0 on success, negative value on error + * + */ +static int sps_rm_free(struct sps_pipe *pipe) +{ + struct sps_connection *map = (void *)pipe->map; + struct sps_connect *cfg = &pipe->connect; + + mutex_lock(&sps_rm->lock); + + /* Free this connection */ + if (cfg->mode == SPS_MODE_SRC) + map->client_src = NULL; + else + map->client_dest = NULL; + + pipe->map = NULL; + pipe->client_state = SPS_STATE_DISCONNECT; + sps_rm_free_map_rsrc(map); + + sps_rm_remove_ref(map); + + mutex_unlock(&sps_rm->lock); + + return 0; +} + +/** + * Allocate an SPS connection end point + * + * This function allocates resources and initializes a BAM connection. + * + * @pipe - client context for SPS connection end point + * + * @return 0 on success, negative value on error + * + */ +static int sps_rm_alloc(struct sps_pipe *pipe) +{ + struct sps_connection *map; + int result = SPS_ERROR; + + if (pipe->connect.sps_reserved != SPSRM_CLEAR) { + /* + * Client did not call sps_get_config() to init + * struct sps_connect, so only use legacy members. + */ + unsigned long source = pipe->connect.source; + unsigned long destination = pipe->connect.destination; + enum sps_mode mode = pipe->connect.mode; + u32 config = pipe->connect.config; + + memset(&pipe->connect, SPSRM_CLEAR, + sizeof(pipe->connect)); + pipe->connect.source = source; + pipe->connect.destination = destination; + pipe->connect.mode = mode; + pipe->connect.config = config; + } + if (pipe->connect.config == SPSRM_CLEAR) + pipe->connect.config = SPS_CONFIG_DEFAULT; + + /* + * If configuration is not default, then client is specifying a + * connection mapping. Find a matching mapping, or fail. + * If a match is found, the client's Connect struct will be updated + * with all the mapping's values. + */ + if (pipe->connect.config != SPS_CONFIG_DEFAULT) { + if (sps_map_find(&pipe->connect)) { + SPS_ERR(sps, + "sps:%s:Failed to find connection mapping\n", + __func__); + return SPS_ERROR; + } + } + + mutex_lock(&sps_rm->lock); + /* Check client state */ + if (IS_SPS_STATE_OK(pipe)) { + SPS_ERR(sps, + "sps:%s:Client connection already allocated\n", + __func__); + goto exit_err; + } + + /* Are the connection resources already allocated? */ + map = find_unconnected(pipe); + if (map != NULL) { + /* Attempt to assign this connection to the client */ + if (sps_rm_assign(pipe, map)) + /* Assignment failed, so must allocate new */ + map = NULL; + } + + /* Allocate a new connection if necessary */ + if (map == NULL) { + map = sps_rm_create(pipe); + if (map == NULL) { + SPS_ERR(sps, + "sps:%s:Failed to allocate connection\n", + __func__); + goto exit_err; + } + list_add_tail(&map->list, &sps_rm->connections_q); + } + + /* Add the connection to the allocated queue */ + map->refs++; + + /* Initialization was successful */ + result = 0; +exit_err: + mutex_unlock(&sps_rm->lock); + + if (result) + return SPS_ERROR; + + return 0; +} + +/** + * Disconnect an SPS connection end point + * + * This function frees resources and de-initializes a BAM connection. + * + * @pipe - client context for SPS connection end point + * + * @return 0 on success, negative value on error + * + */ +static int sps_rm_disconnect(struct sps_pipe *pipe) +{ + sps_rm_free(pipe); + return 0; +} + +/** + * Process connection state change + * + * This function processes a connection state change. + * + * @pipe - pointer to client context + * + * @state - new state for connection + * + * @return 0 on success, negative value on error + * + */ +int sps_rm_state_change(struct sps_pipe *pipe, u32 state) +{ + int auto_enable = false; + int result; + + /* Allocate the pipe */ + if (pipe->client_state == SPS_STATE_DISCONNECT && + state == SPS_STATE_ALLOCATE) { + if (sps_rm_alloc(pipe)) { + SPS_ERR(pipe->bam, + "sps:Fail to allocate resource for BAM 0x%pK pipe %d\n", + pipe->bam, pipe->pipe_index); + return SPS_ERROR; + } + } + + /* Configure the pipe */ + if (pipe->client_state == SPS_STATE_ALLOCATE && + state == SPS_STATE_CONNECT) { + /* Connect the BAM pipe */ + struct sps_bam_connect_param params; + + memset(¶ms, 0, sizeof(params)); + params.mode = pipe->connect.mode; + if (pipe->connect.options != SPSRM_CLEAR) { + params.options = pipe->connect.options; + params.irq_gen_addr = pipe->connect.irq_gen_addr; + params.irq_gen_data = pipe->connect.irq_gen_data; + } + result = sps_bam_pipe_connect(pipe, ¶ms); + if (result) { + SPS_ERR(pipe->bam, + "sps:Failed to connect BAM 0x%pK pipe %d\n", + pipe->bam, pipe->pipe_index); + return SPS_ERROR; + } + pipe->client_state = SPS_STATE_CONNECT; + + /* Set auto-enable for system-mode connections */ + if (pipe->connect.source == SPS_DEV_HANDLE_MEM || + pipe->connect.destination == SPS_DEV_HANDLE_MEM) { + if (pipe->map->desc.size != 0 && + pipe->map->desc.phys_base != SPS_ADDR_INVALID) + auto_enable = true; + } + } + + /* Enable the pipe data flow */ + if (pipe->client_state == SPS_STATE_CONNECT && + !(state == SPS_STATE_DISABLE + || state == SPS_STATE_DISCONNECT) + && (state == SPS_STATE_ENABLE || auto_enable + || (pipe->connect.options & SPS_O_AUTO_ENABLE))) { + result = sps_bam_pipe_enable(pipe->bam, pipe->pipe_index); + if (result) { + SPS_ERR(pipe->bam, + "sps:Failed to set BAM %pa pipe %d flow on\n", + &pipe->bam->props.phys_addr, + pipe->pipe_index); + return SPS_ERROR; + } + + /* Is this a BAM-DMA pipe? */ +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + if ((pipe->bam->props.options & SPS_BAM_OPT_BAMDMA)) { + /* Activate the BAM-DMA channel */ + result = sps_dma_pipe_enable(pipe->bam, + pipe->pipe_index); + if (result) { + SPS_ERR(pipe->bam, + "sps:Failed to activate BAM-DMA pipe: %d\n", + pipe->pipe_index); + return SPS_ERROR; + } + } +#endif + pipe->client_state = SPS_STATE_ENABLE; + } + + /* Disable the pipe data flow */ + if (pipe->client_state == SPS_STATE_ENABLE && + (state == SPS_STATE_DISABLE || state == SPS_STATE_DISCONNECT)) { + result = sps_bam_pipe_disable(pipe->bam, pipe->pipe_index); + if (result) { + SPS_ERR(pipe->bam, + "sps:Failed to set BAM %pa pipe %d flow off\n", + &pipe->bam->props.phys_addr, + pipe->pipe_index); + return SPS_ERROR; + } + pipe->client_state = SPS_STATE_CONNECT; + } + + /* Disconnect the BAM pipe */ + if (pipe->client_state == SPS_STATE_CONNECT && + state == SPS_STATE_DISCONNECT) { + struct sps_connection *map; + struct sps_bam *bam = pipe->bam; + unsigned long flags; + u32 pipe_index; + + if (pipe->connect.mode == SPS_MODE_SRC) + pipe_index = pipe->map->src.pipe_index; + else + pipe_index = pipe->map->dest.pipe_index; + + if (bam->props.irq > 0) + synchronize_irq(bam->props.irq); + + spin_lock_irqsave(&bam->isr_lock, flags); + pipe->disconnecting = true; + spin_unlock_irqrestore(&bam->isr_lock, flags); + result = sps_bam_pipe_disconnect(pipe->bam, pipe_index); + if (result) { + SPS_ERR(pipe->bam, + "sps:Failed to disconnect BAM %pa pipe %d\n", + &pipe->bam->props.phys_addr, + pipe->pipe_index); + return SPS_ERROR; + } + + /* Clear map state */ + map = (void *)pipe->map; + if (pipe->connect.mode == SPS_MODE_SRC) + map->client_src = NULL; + else if (pipe->connect.mode == SPS_MODE_DEST) + map->client_dest = NULL; + + sps_rm_disconnect(pipe); + + /* Clear the client state */ + pipe->map = NULL; + pipe->bam = NULL; + pipe->client_state = SPS_STATE_DISCONNECT; + } + + return 0; +} diff --git a/drivers/soc/qcom/sps/spsi.h b/drivers/soc/qcom/sps/spsi.h new file mode 100644 index 000000000000..7dfcf55cdb5c --- /dev/null +++ b/drivers/soc/qcom/sps/spsi.h @@ -0,0 +1,527 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/** + * Smart-Peripheral-Switch (SPS) internal API. + */ + +#ifndef _SPSI_H_ +#define _SPSI_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include "sps_map.h" + +#if defined(CONFIG_PHYS_ADDR_T_64BIT) || defined(CONFIG_ARM_LPAE) +#define SPS_LPAE (true) +#else +#define SPS_LPAE (false) +#endif + +#define BAM_MAX_PIPES 31 +#define BAM_MAX_P_LOCK_GROUP_NUM 31 + +/* Adjust for offset of struct sps_q_event */ +#define SPS_EVENT_INDEX(e) ((e) - 1) +#define SPS_ERROR -1 + +/* BAM identifier used in log messages */ +#define BAM_ID(dev) (&(dev)->props.phys_addr) + +/* "Clear" value for the connection parameter struct */ +#define SPSRM_CLEAR 0xccccccccUL +#define SPSRM_ADDR_CLR \ + ((sizeof(int) == sizeof(long)) ? 0 : (SPSRM_CLEAR << 32)) + +#define MAX_MSG_LEN 80 +#define SPS_IPC_LOGPAGES 10 +#define SPS_IPC_REG_DUMP_FACTOR 3 +#define SPS_IPC_DEFAULT_LOGLEVEL 3 +#define SPS_IPC_MAX_LOGLEVEL 4 + +/* Connection mapping control struct */ +struct sps_rm { + struct list_head connections_q; + struct mutex lock; +}; + +/* SPS driver state struct */ +struct sps_drv { + struct class *dev_class; + dev_t dev_num; + struct device *dev; + struct clk *pmem_clk; + struct clk *bamdma_clk; + struct clk *dfab_clk; + + int is_ready; + + /* Platform data */ + phys_addr_t pipemem_phys_base; + u32 pipemem_size; + phys_addr_t bamdma_bam_phys_base; + u32 bamdma_bam_size; + phys_addr_t bamdma_dma_phys_base; + u32 bamdma_dma_size; + u32 bamdma_irq; + u32 bamdma_restricted_pipes; + + /* Driver options bitflags (see SPS_OPT_*) */ + u32 options; + + /* Mutex to protect BAM and connection queues */ + struct mutex lock; + + /* BAM devices */ + struct list_head bams_q; + + char *hal_bam_version; + + /* Connection control state */ + struct sps_rm connection_ctrl; + + void *ipc_log0; + void *ipc_log1; + void *ipc_log2; + void *ipc_log3; + void *ipc_log4; + + u32 ipc_loglevel; +}; + +extern struct sps_drv *sps; +extern u32 d_type; +extern bool enhd_pipe; +extern bool imem; +extern enum sps_bam_type bam_type; + +#ifdef CONFIG_DEBUG_FS +extern u8 debugfs_record_enabled; +extern u8 logging_option; +extern u8 debug_level_option; +extern u8 print_limit_option; + +#define SPS_IPC(idx, dev, msg, ...) do { \ + if (dev) { \ + if (idx == 0) \ + ipc_log_string((dev)->ipc_log0, \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else if (idx == 1) \ + ipc_log_string((dev)->ipc_log1, \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else if (idx == 2) \ + ipc_log_string((dev)->ipc_log2, \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else if (idx == 3) \ + ipc_log_string((dev)->ipc_log3, \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else if (idx == 4) \ + ipc_log_string((dev)->ipc_log4, \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + } \ + } while (0) +#define SPS_DUMP(msg, ...) do { \ + SPS_IPC(4, sps, msg, ##__VA_ARGS__); \ + if (sps) { \ + if (sps->ipc_log4 == NULL) \ + pr_info("%s: " msg, __func__, ##__VA_ARGS__); \ + } \ + } while (0) +#define SPS_ERR(dev, msg, ...) do { \ + if (logging_option != 1) { \ + if (unlikely(print_limit_option > 2)) \ + pr_err_ratelimited( \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else \ + pr_err("%s: " msg, __func__, ##__VA_ARGS__); \ + } \ + SPS_IPC(3, dev, msg, ##__VA_ARGS__); \ + } while (0) +#define SPS_INFO(dev, msg, ...) do { \ + if (logging_option != 1) { \ + if (unlikely(print_limit_option > 1)) \ + pr_info_ratelimited( \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else \ + pr_info("%s: " msg, __func__, ##__VA_ARGS__); \ + } \ + SPS_IPC(3, dev, msg, ##__VA_ARGS__); \ + } while (0) +#define SPS_DBG(dev, msg, ...) do { \ + if ((unlikely(logging_option > 1)) \ + && (unlikely(debug_level_option > 3))) {\ + if (unlikely(print_limit_option > 0)) \ + pr_info_ratelimited( \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else \ + pr_info("%s: " msg, __func__, ##__VA_ARGS__); \ + } else \ + pr_debug("%s: " msg, __func__, ##__VA_ARGS__); \ + if (dev) { \ + if ((dev)->ipc_loglevel <= 0) \ + SPS_IPC(0, dev, msg, ##__VA_ARGS__); \ + } \ + } while (0) +#define SPS_DBG1(dev, msg, ...) do { \ + if ((unlikely(logging_option > 1)) \ + && (unlikely(debug_level_option > 2))) {\ + if (unlikely(print_limit_option > 0)) \ + pr_info_ratelimited( \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else \ + pr_info("%s: " msg, __func__, ##__VA_ARGS__); \ + } else \ + pr_debug("%s: " msg, __func__, ##__VA_ARGS__); \ + if (dev) { \ + if ((dev)->ipc_loglevel <= 1) \ + SPS_IPC(1, dev, msg, ##__VA_ARGS__); \ + } \ + } while (0) +#define SPS_DBG2(dev, msg, ...) do { \ + if ((unlikely(logging_option > 1)) \ + && (unlikely(debug_level_option > 1))) {\ + if (unlikely(print_limit_option > 0)) \ + pr_info_ratelimited( \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else \ + pr_info("%s: " msg, __func__, ##__VA_ARGS__); \ + } else \ + pr_debug("%s: " msg, __func__, ##__VA_ARGS__); \ + if (dev) { \ + if ((dev)->ipc_loglevel <= 2) \ + SPS_IPC(2, dev, msg, ##__VA_ARGS__); \ + } \ + } while (0) +#define SPS_DBG3(dev, msg, ...) do { \ + if ((unlikely(logging_option > 1)) \ + && (unlikely(debug_level_option > 0))) {\ + if (unlikely(print_limit_option > 0)) \ + pr_info_ratelimited( \ + "%s: " msg, __func__, ##__VA_ARGS__); \ + else \ + pr_info("%s: " msg, __func__, ##__VA_ARGS__); \ + } else \ + pr_debug("%s: " msg, __func__, ##__VA_ARGS__); \ + if (dev) { \ + if ((dev)->ipc_loglevel <= 3) \ + SPS_IPC(3, dev, msg, ##__VA_ARGS__); \ + } \ + } while (0) +#else +#define SPS_DBG3(dev, msg, args...) pr_debug(msg, ##args) +#define SPS_DBG2(dev, msg, args...) pr_debug(msg, ##args) +#define SPS_DBG1(dev, msg, args...) pr_debug(msg, ##args) +#define SPS_DBG(dev, msg, args...) pr_debug(msg, ##args) +#define SPS_INFO(dev, msg, args...) pr_info(msg, ##args) +#define SPS_ERR(dev, msg, args...) pr_err(msg, ##args) +#define SPS_DUMP(msg, args...) pr_info(msg, ##args) +#endif + +/* End point parameters */ +struct sps_conn_end_pt { + unsigned long dev; /* Device handle of BAM */ + phys_addr_t bam_phys; /* Physical address of BAM. */ + u32 pipe_index; /* Pipe index */ + u32 event_threshold; /* Pipe event threshold */ + u32 lock_group; /* The lock group this pipe belongs to */ + void *bam; +}; + +/* Connection bookkeeping descriptor struct */ +struct sps_connection { + struct list_head list; + + /* Source end point parameters */ + struct sps_conn_end_pt src; + + /* Destination end point parameters */ + struct sps_conn_end_pt dest; + + /* Resource parameters */ + struct sps_mem_buffer desc; /* Descriptor FIFO */ + struct sps_mem_buffer data; /* Data FIFO (BAM-to-BAM mode only) */ + u32 config; /* Client specified connection configuration */ + + /* Connection state */ + void *client_src; + void *client_dest; + int refs; /* Reference counter */ + + /* Dynamically allocated resources, if required */ + u32 alloc_src_pipe; /* Source pipe index */ + u32 alloc_dest_pipe; /* Destination pipe index */ + /* Physical address of descriptor FIFO */ + phys_addr_t alloc_desc_base; + phys_addr_t alloc_data_base; /* Physical address of data FIFO */ +}; + +/* Event bookkeeping descriptor struct */ +struct sps_q_event { + struct list_head list; + /* Event payload data */ + struct sps_event_notify notify; +}; + +/* Memory heap statistics */ +struct sps_mem_stats { + u32 base_addr; + u32 size; + u32 blocks_used; + u32 bytes_used; + u32 max_bytes_used; +}; + +enum sps_bam_type { + SPS_BAM_LEGACY, + SPS_BAM_NDP, + SPS_BAM_NDP_4K +}; + +#ifdef CONFIG_DEBUG_FS +/* record debug info for debugfs */ +void sps_debugfs_record(const char *msg); +#endif + +/* output the content of BAM-level registers */ +void print_bam_reg(void *virt_addr); + +/* output the content of BAM pipe registers */ +void print_bam_pipe_reg(void *virt_addr, u32 pipe_index); + +/* output the content of selected BAM-level registers */ +void print_bam_selected_reg(void *virt_addr, u32 pipe_index); + +/* output the content of selected BAM pipe registers */ +void print_bam_pipe_selected_reg(void *virt_addr, u32 pipe_index); + +/* output descriptor FIFO of a pipe */ +void print_bam_pipe_desc_fifo(void *virt_addr, u32 pipe_index, u32 option); + +/* output BAM_TEST_BUS_REG */ +void print_bam_test_bus_reg(void *base, u32 tb_sel); + +/* halt and un-halt a pipe */ +void bam_pipe_halt(void *base, u32 pipe, bool halt); + +/** + * Translate physical to virtual address + * + * This Function translates physical to virtual address. + * + * @phys_addr - physical address to translate + * + * @return virtual memory pointer + * + */ +void *spsi_get_mem_ptr(phys_addr_t phys_addr); + +/** + * Allocate I/O (pipe) memory + * + * This function allocates target I/O (pipe) memory. + * + * @bytes - number of bytes to allocate + * + * @return physical address of allocated memory, or SPS_ADDR_INVALID on error + */ +phys_addr_t sps_mem_alloc_io(u32 bytes); + +/** + * Free I/O (pipe) memory + * + * This function frees target I/O (pipe) memory. + * + * @phys_addr - physical address of memory to free + * + * @bytes - number of bytes to free. + */ +void sps_mem_free_io(phys_addr_t phys_addr, u32 bytes); + +/** + * Find matching connection mapping + * + * This function searches for a connection mapping that matches the + * parameters supplied by the client. If a match is found, the client's + * parameter struct is updated with the values specified in the mapping. + * + * @connect - pointer to client connection parameters + * + * @return 0 if match is found, negative value otherwise + * + */ +int sps_map_find(struct sps_connect *connect); + +/** + * Allocate a BAM DMA pipe + * + * This function allocates a BAM DMA pipe, and is intended to be called + * internally from the BAM resource manager. Allocation implies that + * the pipe has been referenced by a client Connect() and is in use. + * + * BAM DMA is permissive with activations, and allows a pipe to be allocated + * with or without a client-initiated allocation. This allows the client to + * specify exactly which pipe should be used directly through the Connect() API. + * sps_dma_alloc_chan() does not allow the client to specify the pipes/channel. + * + * @bam - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @dir - pipe direction + * + * @return 0 on success, negative value on error + */ +int sps_dma_pipe_alloc(void *bam, u32 pipe_index, enum sps_mode dir); + +/** + * Enable a BAM DMA pipe + * + * This function enables the channel associated with a BAM DMA pipe, and + * is intended to be called internally from the BAM resource manager. + * Enable must occur *after* the pipe has been enabled so that proper + * sequencing between pipe and DMA channel enables can be enforced. + * + * @bam - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +int sps_dma_pipe_enable(void *bam, u32 pipe_index); + +/** + * Free a BAM DMA pipe + * + * This function disables and frees a BAM DMA pipe, and is intended to be + * called internally from the BAM resource manager. This must occur *after* + * the pipe has been disabled/reset so that proper sequencing between pipe and + * DMA channel resets can be enforced. + * + * @bam_arg - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +int sps_dma_pipe_free(void *bam, u32 pipe_index); + +/** + * Initialize driver memory module + * + * This function initializes the driver memory module. + * + * @pipemem_phys_base - Pipe-Memory physical base. + * + * @pipemem_size - Pipe-Memory size. + * + * @return 0 on success, negative value on error + * + */ +int sps_mem_init(phys_addr_t pipemem_phys_base, u32 pipemem_size); + +/** + * De-initialize driver memory module + * + * This function de-initializes the driver memory module. + * + * @return 0 on success, negative value on error + * + */ +int sps_mem_de_init(void); + +/** + * Initialize BAM DMA module + * + * This function initializes the BAM DMA module. + * + * @bam_props - pointer to BAM DMA devices BSP configuration properties + * + * @return 0 on success, negative value on error + * + */ +int sps_dma_init(const struct sps_bam_props *bam_props); + +/** + * De-initialize BAM DMA module + * + * This function de-initializes the SPS BAM DMA module. + * + */ +void sps_dma_de_init(void); + +/** + * Initialize BAM DMA device + * + * This function initializes a BAM DMA device. + * + * @h - BAM handle + * + * @return 0 on success, negative value on error + * + */ +int sps_dma_device_init(unsigned long h); + +/** + * De-initialize BAM DMA device + * + * This function de-initializes a BAM DMA device. + * + * @h - BAM handle + * + * @return 0 on success, negative value on error + * + */ +int sps_dma_device_de_init(unsigned long h); + +/** + * Initialize connection mapping module + * + * This function initializes the SPS connection mapping module. + * + * @map_props - pointer to connection mapping BSP configuration properties + * + * @options - driver options bitflags (see SPS_OPT_*) + * + * @return 0 on success, negative value on error + * + */ + +int sps_map_init(const struct sps_map *map_props, u32 options); + +/** + * De-initialize connection mapping module + * + * This function de-initializes the SPS connection mapping module. + * + */ +void sps_map_de_init(void); + +/* + * bam_pipe_reset - reset a BAM pipe. + * @base: BAM virtual address + * @pipe: pipe index + * + * This function resets a BAM pipe. + */ +void bam_pipe_reset(void *base, u32 pipe); + +/* + * bam_disable_pipe - disable a BAM pipe. + * @base: BAM virtual address + * @pipe: pipe index + * + * This function disables a BAM pipe. + */ +void bam_disable_pipe(void *base, u32 pipe); +#endif /* _SPSI_H_ */ diff --git a/include/linux/msm-sps.h b/include/linux/msm-sps.h new file mode 100644 index 000000000000..b6ff335f95d1 --- /dev/null +++ b/include/linux/msm-sps.h @@ -0,0 +1,1635 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +/* Smart-Peripheral-Switch (SPS) API. */ + +#ifndef _SPS_H_ +#define _SPS_H_ + +#include +#include + +#if defined(CONFIG_PHYS_ADDR_T_64BIT) || defined(CONFIG_ARM_LPAE) + +/* Returns upper 4bits of 36bits physical address */ +#define SPS_GET_UPPER_ADDR(addr) ((addr & 0xF00000000ULL) >> 32) + +/* Returns 36bits physical address from 32bit address & + * flags word + */ +#define DESC_FULL_ADDR(flags, addr) ((((phys_addr_t)flags & 0xF) << 32) | addr) + +/* Returns flags word with flags and 4bit upper address + * from flags and 36bit physical address + */ +#define DESC_FLAG_WORD(flags, addr) (((addr & 0xF00000000ULL) >> 32) | flags) + +#else + +#define SPS_GET_UPPER_ADDR(addr) (0) +#define DESC_FULL_ADDR(flags, addr) (addr) +#define DESC_FLAG_WORD(flags, addr) (flags) + +#endif + +/* Returns upper 4bits of 36bits physical address from + * flags word + */ +#define DESC_UPPER_ADDR(flags) ((flags & 0xF)) + +/* Returns lower 32bits of 36bits physical address */ +#define SPS_GET_LOWER_ADDR(addr) ((u32)(addr & 0xFFFFFFFF)) + +/* SPS device handle indicating use of system memory */ +#define SPS_DEV_HANDLE_MEM (~0x0ul>>1) + +/* SPS device handle indicating use of BAM-DMA */ + +/* SPS device handle invalid value */ +#define SPS_DEV_HANDLE_INVALID 0 + +/* BAM invalid IRQ value */ +#define SPS_IRQ_INVALID 0 + +/* Invalid address value */ +#define SPS_ADDR_INVALID (0xDEADBEEF) + +/* Invalid peripheral device enumeration class */ +#define SPS_CLASS_INVALID (0xDEADBEEF) + +/* + * This value specifies different configurations for an SPS connection. + * A non-default value instructs the SPS driver to search for the configuration + * in the fixed connection mapping table. + */ +#define SPS_CONFIG_DEFAULT 0 + +/* + * This value instructs the SPS driver to use the default BAM-DMA channel + * threshold + */ +#define SPS_DMA_THRESHOLD_DEFAULT 0 + +/* Flag bits supported by SPS hardware for struct sps_iovec */ +#define SPS_IOVEC_FLAG_INT 0x8000 /* Generate interrupt */ +#define SPS_IOVEC_FLAG_EOT 0x4000 /* Generate end-of-transfer indication */ +#define SPS_IOVEC_FLAG_EOB 0x2000 /* Generate end-of-block indication */ +#define SPS_IOVEC_FLAG_NWD 0x1000 /* notify when done */ +#define SPS_IOVEC_FLAG_CMD 0x0800 /* command descriptor */ +#define SPS_IOVEC_FLAG_LOCK 0x0400 /* pipe lock */ +#define SPS_IOVEC_FLAG_UNLOCK 0x0200 /* pipe unlock */ +#define SPS_IOVEC_FLAG_IMME 0x0100 /* immediate command descriptor */ +#define SPS_IOVEC_FLAG_NO_SUBMIT 0x0020 /* Do not submit descriptor to HW */ +#define SPS_IOVEC_FLAG_DEFAULT 0x0010 /* Use driver default */ + +/* Maximum descriptor/iovec size */ +#define SPS_IOVEC_MAX_SIZE (32 * 1024 - 1) /* 32K-1 bytes due to HW limit */ + +/* BAM device options flags */ + +/* + * BAM will be configured and enabled at boot. Otherwise, BAM will be + * configured and enabled when first pipe connect occurs. + */ +#define SPS_BAM_OPT_ENABLE_AT_BOOT 1UL +/* BAM IRQ is disabled */ +#define SPS_BAM_OPT_IRQ_DISABLED (1UL << 1) +/* BAM peripheral is a BAM-DMA */ +#define SPS_BAM_OPT_BAMDMA (1UL << 2) +/* BAM IRQ is registered for apps wakeup */ +#define SPS_BAM_OPT_IRQ_WAKEUP (1UL << 3) +/* Ignore external block pipe reset */ +#define SPS_BAM_NO_EXT_P_RST (1UL << 4) +/* Don't enable local clock gating */ +#define SPS_BAM_NO_LOCAL_CLK_GATING (1UL << 5) +/* Don't enable writeback cancel*/ +#define SPS_BAM_CANCEL_WB (1UL << 6) +/* BAM uses SMMU */ +#define SPS_BAM_SMMU_EN (1UL << 9) +/* Confirm resource status before access BAM*/ +#define SPS_BAM_RES_CONFIRM (1UL << 7) +/* Hold memory for BAM DMUX */ +#define SPS_BAM_HOLD_MEM (1UL << 8) +/* Use cached write pointer */ +#define SPS_BAM_CACHED_WP (1UL << 10) + +/* BAM device management flags */ + +/* BAM global device control is managed remotely */ +#define SPS_BAM_MGR_DEVICE_REMOTE 1UL +/* BAM device supports multiple execution environments */ +#define SPS_BAM_MGR_MULTI_EE (1UL << 1) +/* BAM pipes are *not* allocated locally */ +#define SPS_BAM_MGR_PIPE_NO_ALLOC (1UL << 2) +/* BAM pipes are *not* configured locally */ +#define SPS_BAM_MGR_PIPE_NO_CONFIG (1UL << 3) +/* BAM pipes are *not* controlled locally */ +#define SPS_BAM_MGR_PIPE_NO_CTRL (1UL << 4) +/* "Globbed" management properties */ +#define SPS_BAM_MGR_NONE \ + (SPS_BAM_MGR_DEVICE_REMOTE | SPS_BAM_MGR_PIPE_NO_ALLOC | \ + SPS_BAM_MGR_PIPE_NO_CONFIG | SPS_BAM_MGR_PIPE_NO_CTRL) +#define SPS_BAM_MGR_LOCAL 0 +#define SPS_BAM_MGR_LOCAL_SHARED SPS_BAM_MGR_MULTI_EE +#define SPS_BAM_MGR_REMOTE_SHARED \ + (SPS_BAM_MGR_DEVICE_REMOTE | SPS_BAM_MGR_MULTI_EE | \ + SPS_BAM_MGR_PIPE_NO_ALLOC) +#define SPS_BAM_MGR_ACCESS_MASK SPS_BAM_MGR_NONE + +/* + * BAM security configuration + */ +#define SPS_BAM_NUM_EES 4 +#define SPS_BAM_SEC_DO_NOT_CONFIG 0 +#define SPS_BAM_SEC_DO_CONFIG 0x0A434553 + +/* BAM pipe selection */ +#define SPS_BAM_PIPE(n) (1UL << (n)) + +/* This enum specifies the operational mode for an SPS connection */ +enum sps_mode { + SPS_MODE_SRC = 0, /* end point is the source (producer) */ + SPS_MODE_DEST, /* end point is the destination (consumer) */ +}; + + +/* + * This enum is a set of bit flag options for SPS connection. + * The enums should be OR'd together to create the option set + * for the SPS connection. + */ +enum sps_option { + /* + * Options to enable specific SPS hardware interrupts. + * These bit flags are also used to indicate interrupt source + * for the SPS_EVENT_IRQ event. + */ + SPS_O_DESC_DONE = 0x00000001, /* Descriptor processed */ + SPS_O_INACTIVE = 0x00000002, /* Inactivity timeout */ + SPS_O_WAKEUP = 0x00000004, /* Peripheral wake up */ + SPS_O_OUT_OF_DESC = 0x00000008,/* Out of descriptors */ + SPS_O_ERROR = 0x00000010, /* Error */ + SPS_O_EOT = 0x00000020, /* End-of-transfer */ + SPS_O_RST_ERROR = 0x00000040, /* Pipe reset unsucessful error */ + SPS_O_HRESP_ERROR = 0x00000080,/* Errorneous Hresponse by AHB MASTER */ + + /* Options to enable hardware features */ + SPS_O_STREAMING = 0x00010000, /* Enable streaming mode (no EOT) */ + /* Use MTI/SETPEND instead of BAM interrupt */ + SPS_O_IRQ_MTI = 0x00020000, + /* NWD bit written with EOT for BAM2BAM producer pipe */ + SPS_O_WRITE_NWD = 0x00040000, + /* EOT set after pipe SW offset advanced */ + SPS_O_LATE_EOT = 0x00080000, + + /* Options to enable software features */ + /* Do not disable a pipe during disconnection */ + SPS_O_NO_DISABLE = 0x00800000, + /* Transfer operation should be polled */ + SPS_O_POLL = 0x01000000, + /* Disable queuing of transfer events for the connection end point */ + SPS_O_NO_Q = 0x02000000, + SPS_O_FLOWOFF = 0x04000000, /* Graceful halt */ + /* SPS_O_WAKEUP will be disabled after triggered */ + SPS_O_WAKEUP_IS_ONESHOT = 0x08000000, + /** + * Client must read each descriptor from the FIFO + * using sps_get_iovec() + */ + SPS_O_ACK_TRANSFERS = 0x10000000, + /* Connection is automatically enabled */ + SPS_O_AUTO_ENABLE = 0x20000000, + /* DISABLE endpoint synchronization for config/enable/disable */ + SPS_O_NO_EP_SYNC = 0x40000000, + /* Allow partial polling duing IRQ mode */ + SPS_O_HYBRID = 0x80000000, + /* Allow dummy BAM connection */ + SPS_O_DUMMY_PEER = 0x00000400, +}; + +/** + * This enum specifies BAM DMA channel priority. Clients should use + * SPS_DMA_PRI_DEFAULT unless a specific priority is required. + */ +enum sps_dma_priority { + SPS_DMA_PRI_DEFAULT = 0, + SPS_DMA_PRI_LOW, + SPS_DMA_PRI_MED, + SPS_DMA_PRI_HIGH, +}; + +/* + * This enum specifies the ownership of a connection resource. + * Remote or shared ownership is only possible/meaningful on the processor + * that controls resource. + */ +enum sps_owner { + SPS_OWNER_LOCAL = 0x1, /* Resource is owned by local processor */ + SPS_OWNER_REMOTE = 0x2, /* Resource is owned by a satellite processor */ +}; + +/* This enum indicates the event associated with a client event trigger */ +enum sps_event { + SPS_EVENT_INVALID = 0, + + SPS_EVENT_EOT, /* End-of-transfer */ + SPS_EVENT_DESC_DONE, /* Descriptor processed */ + SPS_EVENT_OUT_OF_DESC, /* Out of descriptors */ + SPS_EVENT_WAKEUP, /* Peripheral wake up */ + SPS_EVENT_FLOWOFF, /* Graceful halt (idle) */ + SPS_EVENT_INACTIVE, /* Inactivity timeout */ + SPS_EVENT_ERROR, /* Error */ + SPS_EVENT_RST_ERROR, /* Pipe Reset unsuccessful */ + SPS_EVENT_HRESP_ERROR, /* Errorneous Hresponse by AHB Master*/ + SPS_EVENT_MAX, +}; + +/* + * This enum specifies the event trigger mode and is an argument for the + * sps_register_event() function. + */ +enum sps_trigger { + /* Trigger with payload for callback */ + SPS_TRIGGER_CALLBACK = 0, + /* Trigger without payload for wait or poll */ + SPS_TRIGGER_WAIT, +}; + +/* + * This enum indicates the desired halting mechanism and is an argument for the + * sps_flow_off() function + */ +enum sps_flow_off { + SPS_FLOWOFF_FORCED = 0, /* Force hardware into halt state */ + /* Allow hardware to empty pipe before halting */ + SPS_FLOWOFF_GRACEFUL, +}; + +/* + * This enum indicates the target memory heap and is an argument for the + * sps_mem_alloc() function. + */ +enum sps_mem { + SPS_MEM_LOCAL = 0, /* SPS subsystem local (pipe) memory */ + SPS_MEM_UC, /* Microcontroller (ARM7) local memory */ +}; + +/* + * This enum indicates a timer control operation and is an argument for the + * sps_timer_ctrl() function. + */ +enum sps_timer_op { + SPS_TIMER_OP_CONFIG = 0, + SPS_TIMER_OP_RESET, +/* SPS_TIMER_OP_START, Not supported by hardware yet */ +/* SPS_TIMER_OP_STOP, Not supported by hardware yet */ + SPS_TIMER_OP_READ, +}; + +/* + * This enum indicates the inactivity timer operating mode and is an + * argument for the sps_timer_ctrl() function. + */ +enum sps_timer_mode { + SPS_TIMER_MODE_ONESHOT = 0, +/* SPS_TIMER_MODE_PERIODIC, Not supported by hardware yet */ +}; + +/* This enum indicates the cases when callback the user of BAM */ +enum sps_callback_case { + SPS_CALLBACK_BAM_ERROR_IRQ = 1, /* BAM ERROR IRQ */ + SPS_CALLBACK_BAM_HRESP_ERR_IRQ, /* Erroneous HResponse */ + SPS_CALLBACK_BAM_TIMER_IRQ, /* Inactivity timer */ + SPS_CALLBACK_BAM_RES_REQ, /* Request resource */ + SPS_CALLBACK_BAM_RES_REL, /* Release resource */ + SPS_CALLBACK_BAM_POLL, /* To poll each pipe */ +}; + +/* + * This enum indicates the command type in a command element + */ +enum sps_command_type { + SPS_WRITE_COMMAND = 0, + SPS_READ_COMMAND, +}; + +/** + * struct msm_sps_platform_data - SPS Platform specific data. + * @bamdma_restricted_pipes - Bitmask of pipes restricted from local use. + * + */ +struct msm_sps_platform_data { + u32 bamdma_restricted_pipes; +}; + +/** + * This data type corresponds to the native I/O vector (BAM descriptor) + * supported by SPS hardware + * + * @addr - Buffer physical address. + * @size - Buffer size in bytes. + * @flags -Flag bitmask (see SPS_IOVEC_FLAG_ #defines). + * + */ +struct sps_iovec { + u32 addr; + u32 size:16; + u32 flags:16; +}; + +/** + * This data type corresponds to the native Command Element + * supported by SPS hardware + * + * @addr - register address. + * @command - command type. + * @data - for write command: content to be written into peripheral register. + * for read command: dest addr to write peripheral register value to. + * @mask - register mask. + * @reserved - for future usage. + * + */ +struct sps_command_element { + u32 addr:24; + u32 command:8; + u32 data; + u32 mask; + u32 reserved; +}; + +/* + * BAM device's security configuration + */ +struct sps_bam_pipe_sec_config_props { + u32 pipe_mask; + u32 vmid; +}; + +struct sps_bam_sec_config_props { + /* Per-EE configuration - This is a pipe bit mask for each EE */ + struct sps_bam_pipe_sec_config_props ees[SPS_BAM_NUM_EES]; +}; + +/** + * This struct defines a BAM device. The client must memset() this struct to + * zero before writing device information. A value of zero for uninitialized + * values will instruct the SPS driver to use general defaults or + * hardware/BIOS supplied values. + * + * + * @options - See SPS_BAM_OPT_* bit flag. + * @phys_addr - BAM base physical address (not peripheral address). + * @virt_addr - BAM base virtual address. + * @virt_size - For virtual mapping. + * @irq - IRQ enum for use in ISR vector install. + * @num_pipes - number of pipes. Can be read from hardware. + * @summing_threshold - BAM event threshold. + * + * @periph_class - Peripheral device enumeration class. + * @periph_dev_id - Peripheral global device ID. + * @periph_phys_addr - Peripheral base physical address, for BAM-DMA only. + * @periph_virt_addr - Peripheral base virtual address. + * @periph_virt_size - Size for virtual mapping. + * + * @callback - callback function for BAM user. + * @user - pointer to user data. + * + * @event_threshold - Pipe event threshold. + * @desc_size - Size (bytes) of descriptor FIFO. + * @data_size - Size (bytes) of data FIFO. + * @desc_mem_id - Heap ID for default descriptor FIFO allocations. + * @data_mem_id - Heap ID for default data FIFO allocations. + * + * @manage - BAM device management flags (see SPS_BAM_MGR_*). + * @restricted_pipes - Bitmask of pipes restricted from local use. + * @ee - Local execution environment index. + * + * @irq_gen_addr - MTI interrupt generation address. This configuration only + * applies to BAM rev 1 and 2 hardware. MTIs are only supported on BAMs when + * global config is controlled by a remote processor. + * NOTE: This address must correspond to the MTI associated with the "irq" IRQ + * enum specified above. + * + * @sec_config - must be set to SPS_BAM_SEC_DO_CONFIG to perform BAM security + * configuration. Only the processor that manages the BAM is allowed to + * perform the configuration. The global (top-level) BAM interrupt will be + * assigned to the EE of the processor that manages the BAM. + * + * @p_sec_config_props - BAM device's security configuration + * + */ +struct sps_bam_props { + + /* BAM device properties. */ + + u32 options; + phys_addr_t phys_addr; + void __iomem *virt_addr; + u32 virt_size; + u32 irq; + u32 num_pipes; + u32 summing_threshold; + + /* Peripheral device properties */ + + u32 periph_class; + u32 periph_dev_id; + phys_addr_t periph_phys_addr; + void *periph_virt_addr; + u32 periph_virt_size; + + /* Connection pipe parameter defaults. */ + + u32 event_threshold; + u32 desc_size; + u32 data_size; + u32 desc_mem_id; + u32 data_mem_id; + + /* Feedback to BAM user */ + void (*callback)(enum sps_callback_case, void *user); + void *user; + + /* Security properties */ + + u32 manage; + u32 restricted_pipes; + u32 ee; + + /* Log Level property */ + u32 ipc_loglevel; + + /* BAM MTI interrupt generation */ + + u32 irq_gen_addr; + + /* Security configuration properties */ + + u32 sec_config; + struct sps_bam_sec_config_props *p_sec_config_props; + + /* Logging control */ + + bool constrained_logging; + u32 logging_number; +}; + +/** + * This struct specifies memory buffer properties. + * + * @base - Buffer virtual address. + * @phys_base - Buffer physical address. + * @size - Specifies buffer size (or maximum size). + * @min_size - If non-zero, specifies buffer minimum size. + * + */ +struct sps_mem_buffer { + void *base; + phys_addr_t phys_base; + unsigned long iova; + u32 size; + u32 min_size; +}; + +/** + * This struct defines a connection's end point and is used as the argument + * for the sps_connect(), sps_get_config(), and sps_set_config() functions. + * For system mode pipe, use SPS_DEV_HANDLE_MEM for the end point that + * corresponds to system memory. + * + * The client can force SPS to reserve a specific pipe on a BAM. + * If the pipe is in use, the sps_connect/set_config() will fail. + * + * @source - Source BAM. + * @src_pipe_index - BAM pipe index, 0 to 30. + * @destination - Destination BAM. + * @dest_pipe_index - BAM pipe index, 0 to 30. + * + * @mode - specifies which end (source or destination) of the connection will + * be controlled/referenced by the client. + * + * @config - This value is for future use and should be set to + * SPS_CONFIG_DEFAULT or left as default from sps_get_config(). + * + * @options - OR'd connection end point options (see SPS_O defines). + * + * WARNING: The memory provided should be physically contiguous and non-cached. + * The user can use one of the following: + * 1. sps_alloc_mem() - allocated from pipe-memory. + * 2. dma_alloc_coherent() - allocate coherent DMA memory. + * 3. dma_map_single() - for using memory allocated by kmalloc(). + * + * @desc - Descriptor FIFO. + * @data - Data FIFO (BAM-to-BAM mode only). + * + * @event_thresh - Pipe event threshold or derivative. + * @lock_group - The lock group this pipe belongs to. + * + * @sps_reserved - Reserved word - client must not modify. + * + */ +struct sps_connect { + unsigned long source; + unsigned long source_iova; + u32 src_pipe_index; + unsigned long destination; + unsigned long dest_iova; + u32 dest_pipe_index; + + enum sps_mode mode; + + u32 config; + + enum sps_option options; + + struct sps_mem_buffer desc; + struct sps_mem_buffer data; + + u32 event_thresh; + + u32 lock_group; + + /* SETPEND/MTI interrupt generation parameters */ + + u32 irq_gen_addr; + u32 irq_gen_data; + + u32 sps_reserved; + +}; + +/** + * This struct defines a satellite connection's end point. The client of the + * SPS driver on the satellite processor must call sps_get_config() to + * initialize a struct sps_connect, then copy the values from the struct + * sps_satellite to the struct sps_connect before making the sps_connect() + * call to the satellite SPS driver. + * + */ +struct sps_satellite { + /** + * These values must be copied to either the source or destination + * corresponding values in the connect struct. + */ + phys_addr_t dev; + u32 pipe_index; + + /** + * These values must be copied to the corresponding values in the + * connect struct + */ + u32 config; + enum sps_option options; + +}; + +/** + * This struct defines parameters for allocation of a BAM DMA channel. The + * client must memset() this struct to zero before writing allocation + * information. A value of zero for uninitialized values will instruct + * the SPS driver to use defaults or "don't care". + * + * @dev - Associated BAM device handle, or SPS_DEV_HANDLE_DMA. + * + * @src_owner - Source owner processor ID. + * @dest_owner - Destination owner processor ID. + * + */ +struct sps_alloc_dma_chan { + unsigned long dev; + + /* BAM DMA channel configuration parameters */ + + u32 threshold; + enum sps_dma_priority priority; + + /** + * Owner IDs are global host processor identifiers used by the system + * SROT when establishing execution environments. + */ + u32 src_owner; + u32 dest_owner; + +}; + +/** + * This struct defines parameters for an allocated BAM DMA channel. + * + * @dev - BAM DMA device handle. + * @dest_pipe_index - Destination/input/write pipe index. + * @src_pipe_index - Source/output/read pipe index. + * + */ +struct sps_dma_chan { + unsigned long dev; + u32 dest_pipe_index; + u32 src_pipe_index; +}; + +/** + * This struct is an argument passed payload when triggering a callback event + * object registered for an SPS connection end point. + * + * @user - Pointer registered with sps_register_event(). + * + * @event_id - Which event. + * + * @iovec - The associated I/O vector. If the end point is a system-mode + * producer, the size will reflect the actual number of bytes written to the + * buffer by the pipe. NOTE: If this I/O vector was part of a set submitted to + * sps_transfer(), then the vector array itself will be updated with all of + * the actual counts. + * + * @user - Pointer registered with the transfer. + * + */ +struct sps_event_notify { + void *user; + + enum sps_event event_id; + + /* Data associated with the event */ + + union { + /* Data for SPS_EVENT_IRQ */ + struct { + u32 mask; + } irq; + + /* Data for SPS_EVENT_EOT or SPS_EVENT_DESC_DONE */ + + struct { + struct sps_iovec iovec; + void *user; + } transfer; + + /* Data for SPS_EVENT_ERROR */ + + struct { + u32 status; + } err; + + } data; +}; + +/** + * This struct defines a event registration parameters and is used as the + * argument for the sps_register_event() function. + * + * @options - Event options that will trigger the event object. + * @mode - Event trigger mode. + * + * @xfer_done - a pointer to a completion object. NULL if not in use. + * + * @callback - a callback to call on completion. NULL if not in use. + * + * @user - User pointer that will be provided in event callback data. + * + */ +struct sps_register_event { + enum sps_option options; + enum sps_trigger mode; + struct completion *xfer_done; + void (*callback)(struct sps_event_notify *notify); + void *user; +}; + +/** + * This struct defines a system memory transfer's parameters and is used as the + * argument for the sps_transfer() function. + * + * @iovec_phys - Physical address of I/O vectors buffer. + * @iovec - Pointer to I/O vectors buffer. + * @iovec_count - Number of I/O vectors. + * @user - User pointer passed in callback event. + * + */ +struct sps_transfer { + phys_addr_t iovec_phys; + struct sps_iovec *iovec; + u32 iovec_count; + void *user; +}; + +/** + * This struct defines a timer control operation parameters and is used as an + * argument for the sps_timer_ctrl() function. + * + * @op - Timer control operation. + * @timeout_msec - Inactivity timeout (msec). + * + */ +struct sps_timer_ctrl { + enum sps_timer_op op; + + /** + * The following configuration parameters must be set when the timer + * control operation is SPS_TIMER_OP_CONFIG. + */ + enum sps_timer_mode mode; + u32 timeout_msec; +}; + +/** + * This struct defines a timer control operation result and is used as an + * argument for the sps_timer_ctrl() function. + */ +struct sps_timer_result { + u32 current_timer; +}; + + +/*---------------------------------------------------------------------------- + * Functions specific to sps interface + * --------------------------------------------------------------------------- + */ +struct sps_pipe; /* Forward declaration */ + +#if IS_ENABLED(CONFIG_SPS) +/** + * Register a BAM device + * + * This function registers a BAM device with the SPS driver. For each + *peripheral that includes a BAM, the peripheral driver must register + * the BAM with the SPS driver. + * + * A requirement is that the peripheral driver must remain attached + * to the SPS driver until the BAM is deregistered. Otherwise, the + * system may attempt to unload the SPS driver. BAM registrations would + * be lost. + * + * @bam_props - Pointer to struct for BAM device properties. + * + * @dev_handle - Device handle will be written to this location (output). + * + * @return 0 on success, negative value on error + * + */ +int sps_register_bam_device(const struct sps_bam_props *bam_props, + unsigned long *dev_handle); + +/** + * Deregister a BAM device + * + * This function deregisters a BAM device from the SPS driver. The peripheral + * driver should deregister a BAM when the peripheral driver is shut down or + * when BAM use should be disabled. + * + * A BAM cannot be deregistered if any of its pipes is in an active connection. + * + * When all BAMs have been deregistered, the system is free to unload the + * SPS driver. + * + * @dev_handle - BAM device handle. + * + * @return 0 on success, negative value on error + * + */ +int sps_deregister_bam_device(unsigned long dev_handle); + +/** + * Allocate client state context + * + * This function allocate and initializes a client state context struct. + * + * @return pointer to client state context + * + */ +struct sps_pipe *sps_alloc_endpoint(void); + +/** + * Free client state context + * + * This function de-initializes and free a client state context struct. + * + * @ctx - client context for SPS connection end point + * + * @return 0 on success, negative value on error + * + */ +int sps_free_endpoint(struct sps_pipe *h); + +/** + * Get the configuration parameters for an SPS connection end point + * + * This function retrieves the configuration parameters for an SPS connection + * end point. + * This function may be called before the end point is connected (before + * sps_connect is called). This allows the client to specify parameters before + * the connection is established. + * + * The client must call this function to fill it's struct sps_connect + * struct before modifying values and passing the struct to sps_set_config(). + * + * @h - client context for SPS connection end point + * + * @config - Pointer to buffer for the end point's configuration parameters. + * Must not be NULL. + * + * @return 0 on success, negative value on error + * + */ +int sps_get_config(struct sps_pipe *h, struct sps_connect *config); + +/** + * Allocate memory from the SPS Pipe-Memory. + * + * @h - client context for SPS connection end point + * + * @mem - memory type - N/A. + * + * @mem_buffer - Pointer to struct for allocated memory properties. + * + * @return 0 on success, negative value on error + * + */ +int sps_alloc_mem(struct sps_pipe *h, enum sps_mem mem, + struct sps_mem_buffer *mem_buffer); + +/** + * Free memory from the SPS Pipe-Memory. + * + * @h - client context for SPS connection end point + * + * @mem_buffer - Pointer to struct for allocated memory properties. + * + * @return 0 on success, negative value on error + * + */ +int sps_free_mem(struct sps_pipe *h, struct sps_mem_buffer *mem_buffer); + +/** + * Connect an SPS connection end point + * + * This function creates a connection between two SPS peripherals or between + * an SPS peripheral and the local host processor (via system memory, end + *point SPS_DEV_HANDLE_MEM). Establishing the connection includes + * initialization of the SPS hardware and allocation of any other connection + * resources (buffer memory, etc.). + * + * This function requires the client to specify both the source and + * destination end points of the SPS connection. However, the handle + * returned applies only to the end point of the connection that the client + * controls. The end point under control must be specified by the + * enum sps_mode mode argument, either SPS_MODE_SRC, SPS_MODE_DEST, or + * SPS_MODE_CTL. Note that SPS_MODE_CTL is only supported for I/O + * accelerator connections, and only a limited set of control operations are + * allowed (TBD). + * + * For a connection involving system memory + * (SPS_DEV_HANDLE_MEM), the peripheral end point must be + * specified. For example, SPS_MODE_SRC must be specified for a + * BAM-to-system connection, since the BAM pipe is the data + * producer. + * + * For a specific peripheral-to-peripheral connection, there may be more than + * one required configuration. For example, there might be high-performance + * and low-power configurations for a connection between the two peripherals. + * The config argument allows the client to specify different configurations, + * which may require different system resource allocations and hardware + * initialization. + * + * A client is allowed to create one and only one connection for its + * struct sps_pipe. The handle is used to identify the connection end point + * in subsequent SPS driver calls. A specific connection source or + * destination end point can be associated with one and only one + * struct sps_pipe. + * + * The client must establish an open device handle to the SPS. To do so, the + * client must attach to the SPS driver and open the SPS device by calling + * the following functions. + * + * @h - client context for SPS connection end point + * + * @connect - Pointer to connection parameters + * + * @return 0 on success, negative value on error + * + */ +int sps_connect(struct sps_pipe *h, struct sps_connect *connect); + +/** + * Disconnect an SPS connection end point + * + * This function disconnects an SPS connection end point. + * The SPS hardware associated with that end point will be disabled. + * For a connection involving system memory (SPS_DEV_HANDLE_MEM), all + * connection resources are deallocated. For a peripheral-to-peripheral + * connection, the resources associated with the connection will not be + * deallocated until both end points are closed. + * + * The client must call sps_connect() for the handle before calling + * this function. + * + * @h - client context for SPS connection end point + * + * @return 0 on success, negative value on error + * + */ +int sps_disconnect(struct sps_pipe *h); + +/** + * Register an event object for an SPS connection end point + * + * This function registers a callback event object for an SPS connection end + *point. The registered event object will be triggered for the set of + * events specified in reg->options that are enabled for the end point. + * + * There can only be one registered event object for each event. If an event + * object is already registered for an event, it will be replaced. If + *reg->event handle is NULL, then any registered event object for the + * event will be deregistered. Option bits in reg->options not associated + * with events are ignored. + * + * The client must call sps_connect() for the handle before calling + * this function. + * + * @h - client context for SPS connection end point + * + * @reg - Pointer to event registration parameters + * + * @return 0 on success, negative value on error + * + */ +int sps_register_event(struct sps_pipe *h, struct sps_register_event *reg); + +/** + * Perform a single DMA transfer on an SPS connection end point + * + * This function submits a DMA transfer request consisting of a single buffer + * for an SPS connection end point associated with a peripheral-to/from-memory + * connection. The request will be submitted immediately to hardware if the + * hardware is idle (data flow off, no other pending transfers). Otherwise, it + * will be queued for later handling in the SPS driver work loop. + * + * The data buffer must be DMA ready. The client is responsible for insuring + *physically contiguous memory, cache maintenance, and memory barrier. For + * more information, see Appendix A. + * + * The client must not modify the data buffer until the completion indication is + * received. + * + * This function cannot be used if transfer queuing is disabled (see option + * SPS_O_NO_Q). The client must set the SPS_O_EOT option to receive a callback + * event trigger when the transfer is complete. The SPS driver will insure the + * appropriate flags in the I/O vectors are set to generate the completion + * indication. + * + * The return value from this function may indicate that an error occurred. + * Possible causes include invalid arguments. + * + * @h - client context for SPS connection end point + * + * @addr - Physical address of buffer to transfer. + * + * WARNING: The memory provided should be physically contiguous and + * non-cached. + * + * The user can use one of the following: + * 1. sps_alloc_mem() - allocated from pipe-memory. + * 2. dma_alloc_coherent() - allocate DMA memory. + * 3. dma_map_single() for memory allocated by kmalloc(). + * + * @size - Size in bytes of buffer to transfer + * + * @user - User pointer that will be returned to user as part of + * event payload + * + * @return 0 on success, negative value on error + * + */ +int sps_transfer_one(struct sps_pipe *h, phys_addr_t addr, u32 size, + void *user, u32 flags); + +/** + * Read event queue for an SPS connection end point + * + * This function reads event queue for an SPS connection end point. + * + * @h - client context for SPS connection end point + * + * @event - pointer to client's event data buffer + * + * @return 0 on success, negative value on error + * + */ +int sps_get_event(struct sps_pipe *h, struct sps_event_notify *event); + +/** + * Get processed I/O vector (completed transfers) + * + * This function fetches the next processed I/O vector. + * + * @h - client context for SPS connection end point + * + * @iovec - Pointer to I/O vector struct (output). + * This struct will be zeroed if there are no more processed I/O vectors. + * + * @return 0 on success, negative value on error + * + */ +int sps_get_iovec(struct sps_pipe *h, struct sps_iovec *iovec); + +/** + * Enable an SPS connection end point + * + * This function enables an SPS connection end point. + * + * @h - client context for SPS connection end point + * + * @return 0 on success, negative value on error + * + */ +int sps_flow_on(struct sps_pipe *h); + +/** + * Disable an SPS connection end point + * + * This function disables an SPS connection end point. + * + * @h - client context for SPS connection end point + * + * @mode - Desired mode for disabling pipe data flow + * + * @return 0 on success, negative value on error + * + */ +int sps_flow_off(struct sps_pipe *h, enum sps_flow_off mode); + +/** + * Perform a Multiple DMA transfer on an SPS connection end point + * + * This function submits a DMA transfer request for an SPS connection end point + * associated with a peripheral-to/from-memory connection. The request will be + * submitted immediately to hardware if the hardware is idle (data flow off, no + * other pending transfers). Otherwise, it will be queued for later handling in + * the SPS driver work loop. + * + * The data buffers referenced by the I/O vectors must be DMA ready. + * The client is responsible for insuring physically contiguous memory, + * any cache maintenance, and memory barrier. For more information, + * see Appendix A. + * + * The I/O vectors must specify physical addresses for the referenced buffers. + * + * The client must not modify the data buffers referenced by I/O vectors until + * the completion indication is received. + * + * If transfer queuing is disabled (see option SPS_O_NO_Q), the client is + * responsible for setting the appropriate flags in the I/O vectors to generate + * the completion indication. Also, the client is responsible for enabling the + * appropriate connection callback event options for completion indication (see + * sps_connect(), sps_set_config()). + * + * If transfer queuing is enabled, the client must set the SPS_O_EOT option to + * receive a callback event trigger when the transfer is complete. The SPS + * driver will insure the appropriate flags in the I/O vectors are set to + * generate the completion indication. The client must not set any flags in the + * I/O vectors, as this may cause the SPS driver to become out of sync with the + * hardware. + * + * The return value from this function may indicate that an error occurred. + * Possible causes include invalid arguments. If transfer queuing is disabled, + * an error will occur if the pipe is already processing a transfer. + * + * @h - client context for SPS connection end point + * + * @transfer - Pointer to transfer parameter struct + * + * @return 0 on success, negative value on error + * + */ +int sps_transfer(struct sps_pipe *h, struct sps_transfer *transfer); + +/** + * Determine whether an SPS connection end point FIFO is empty + * + * This function returns the empty state of an SPS connection end point. + * + * @h - client context for SPS connection end point + * + * @empty - pointer to client's empty status word (boolean) + * + * @return 0 on success, negative value on error + * + */ +int sps_is_pipe_empty(struct sps_pipe *h, u32 *empty); + +/** + * Reset an SPS BAM device + * + * This function resets an SPS BAM device. + * + * @dev - device handle for the BAM + * + * @return 0 on success, negative value on error + * + */ +int sps_device_reset(unsigned long dev); + +/** + * Set the configuration parameters for an SPS connection end point + * + * This function sets the configuration parameters for an SPS connection + * end point. This function may be called before the end point is connected + * (before sps_connect is called). This allows the client to specify + *parameters before the connection is established. The client is allowed + * to pre-allocate resources and override driver defaults. + * + * The client must call sps_get_config() to fill it's struct sps_connect + * struct before modifying values and passing the struct to this function. + * Only those parameters that differ from the current configuration will + * be processed. + * + * @h - client context for SPS connection end point + * + * @config - Pointer to the end point's new configuration parameters. + * + * @return 0 on success, negative value on error + * + */ +int sps_set_config(struct sps_pipe *h, struct sps_connect *config); + +/** + * Set ownership of an SPS connection end point + * + * This function sets the ownership of an SPS connection end point to + * either local (default) or non-local. This function is used to + * retrieve the struct sps_connect data that must be used by a + * satellite processor when calling sps_connect(). + * + * Non-local ownership is only possible/meaningful on the processor + * that controls resource allocations (apps processor). Setting ownership + * to non-local on a satellite processor will fail. + * + * Setting ownership from non-local to local will succeed only if the + * owning satellite processor has properly brought the end point to + * an idle condition. + * + * This function will succeed if the connection end point is already in + * the specified ownership state. + * + * @h - client context for SPS connection end point + * + * @owner - New ownership of the connection end point + * + * @connect - Pointer to buffer for satellite processor connect data. + * Can be NULL to avoid retrieving the connect data. Will be ignored + * if the end point ownership is set to local. + * + * @return 0 on success, negative value on error + * + */ +int sps_set_owner(struct sps_pipe *h, enum sps_owner owner, + struct sps_satellite *connect); + +#ifdef CONFIG_SPS_SUPPORT_BAMDMA +/** + * Allocate a BAM DMA channel + * + * This function allocates a BAM DMA channel. A "BAM DMA" is a special + * DMA peripheral with a BAM front end. The DMA peripheral acts as a conduit + * for data to flow into a consumer pipe and then out of a producer pipe. + * It's primarily purpose is to serve as a path for interprocessor communication + * that allows each processor to control and protect it's own memory space. + * + * @alloc - Pointer to struct for BAM DMA channel allocation properties. + * + * @chan - Allocated channel information will be written to this + * location (output). + * + * @return 0 on success, negative value on error + * + */ +int sps_alloc_dma_chan(const struct sps_alloc_dma_chan *alloc, + struct sps_dma_chan *chan); + +/** + * Free a BAM DMA channel + * + * This function frees a BAM DMA channel. + * + * @chan - Pointer to information for channel to free + * + * @return 0 on success, negative value on error + * + */ +int sps_free_dma_chan(struct sps_dma_chan *chan); + +/** + * Get the BAM handle for BAM-DMA. + * + * The BAM handle should be use as source/destination in the sps_connect(). + * + * @return handle on success, zero on error + * + */ +unsigned long sps_dma_get_bam_handle(void); + +/** + * Free the BAM handle for BAM-DMA. + * + */ +void sps_dma_free_bam_handle(unsigned long h); +#else +static inline int sps_alloc_dma_chan(const struct sps_alloc_dma_chan *alloc, + struct sps_dma_chan *chan) +{ + return -EPERM; +} + +static inline int sps_free_dma_chan(struct sps_dma_chan *chan) +{ + return -EPERM; +} + +static inline unsigned long sps_dma_get_bam_handle(void) +{ + return 0; +} + +static inline void sps_dma_free_bam_handle(unsigned long h) +{ +} +#endif + +/** + * Get number of free transfer entries for an SPS connection end point + * + * This function returns the number of free transfer entries for an + * SPS connection end point. + * + * @h - client context for SPS connection end point + * + * @count - pointer to count status + * + * @return 0 on success, negative value on error + * + */ +int sps_get_free_count(struct sps_pipe *h, u32 *count); + +/** + * Perform timer control + * + * This function performs timer control operations. + * + * @h - client context for SPS connection end point + * + * @timer_ctrl - Pointer to timer control specification + * + * @timer_result - Pointer to buffer for timer operation result. + * This argument can be NULL if no result is expected for the operation. + * If non-NULL, the current timer value will always provided. + * + * @return 0 on success, negative value on error + * + */ +int sps_timer_ctrl(struct sps_pipe *h, + struct sps_timer_ctrl *timer_ctrl, + struct sps_timer_result *timer_result); + +/** + * Find the handle of a BAM device based on the physical address + * + * This function finds a BAM device in the BAM registration list that + * matches the specified physical address, and returns its handle. + * + * @phys_addr - physical address of the BAM + * + * @h - device handle of the BAM + * + * @return 0 on success, negative value on error + * + */ +int sps_phy2h(phys_addr_t phys_addr, unsigned long *handle); + +/** + * Setup desc/data FIFO for bam-to-bam connection + * + * @mem_buffer - Pointer to struct for allocated memory properties. + * + * @addr - address of FIFO + * + * @size - FIFO size + * + * @use_offset - use address offset instead of absolute address + * + * @return 0 on success, negative value on error + * + */ +int sps_setup_bam2bam_fifo(struct sps_mem_buffer *mem_buffer, + u32 addr, u32 size, int use_offset); + +/** + * Get the number of unused descriptors in the descriptor FIFO + * of a pipe + * + * @h - client context for SPS connection end point + * + * @desc_num - number of unused descriptors + * + * @return 0 on success, negative value on error + * + */ +int sps_get_unused_desc_num(struct sps_pipe *h, u32 *desc_num); + +/** + * Get the debug info of BAM registers and descriptor FIFOs + * + * @dev - BAM device handle + * + * @option - debugging option + * + * @para - parameter used for an option (such as pipe combination) + * + * @tb_sel - testbus selection + * + * @desc_sel - selection of descriptors + * + * @return 0 on success, negative value on error + * + */ +int sps_get_bam_debug_info(unsigned long dev, u32 option, u32 para, + u32 tb_sel, u32 desc_sel); + +/** + * Vote for or relinquish BAM DMA clock + * + * @clk_on - to turn on or turn off the clock + * + * @return 0 on success, negative value on error + * + */ +int sps_ctrl_bam_dma_clk(bool clk_on); + +/* + * sps_pipe_reset - reset a pipe of a BAM. + * @dev: BAM device handle + * @pipe: pipe index + * + * This function resets a pipe of a BAM. + * + * Return: 0 on success, negative value on error + */ +int sps_pipe_reset(unsigned long dev, u32 pipe); + +/* + * sps_pipe_disable - disable a pipe of a BAM. + * @dev: BAM device handle + * @pipe: pipe index + * + * This function disables a pipe of a BAM. + * + * Return: 0 on success, negative value on error + */ +int sps_pipe_disable(unsigned long dev, u32 pipe); + +/* + * sps_pipe_pending_desc - checking pending descriptor. + * @dev: BAM device handle + * @pipe: pipe index + * @pending: indicate if there is any pending descriptor. + * + * This function checks if a pipe of a BAM has any pending descriptor. + * + * Return: 0 on success, negative value on error + */ +int sps_pipe_pending_desc(unsigned long dev, u32 pipe, bool *pending); + +/* + * sps_bam_process_irq - process IRQ of a BAM. + * @dev: BAM device handle + * + * This function processes any pending IRQ of a BAM. + * + * Return: 0 on success, negative value on error + */ +int sps_bam_process_irq(unsigned long dev); + +/* + * sps_get_bam_addr - get address info of a BAM. + * @dev: BAM device handle + * @base: beginning address + * @size: address range size + * + * This function returns the address info of a BAM. + * + * Return: 0 on success, negative value on error + */ +int sps_get_bam_addr(unsigned long dev, phys_addr_t *base, + u32 *size); + +/* + * sps_pipe_inject_zlt - inject a ZLT with EOT. + * @dev: BAM device handle + * @pipe_index: pipe index + * + * This function injects a ZLT with EOT for a pipe of a BAM. + * + * Return: 0 on success, negative value on error + */ +int sps_pipe_inject_zlt(unsigned long dev, u32 pipe_index); +#else +static inline int sps_register_bam_device(const struct sps_bam_props + *bam_props, unsigned long *dev_handle) +{ + return -EPERM; +} + +static inline int sps_deregister_bam_device(unsigned long dev_handle) +{ + return -EPERM; +} + +static inline struct sps_pipe *sps_alloc_endpoint(void) +{ + return NULL; +} + +static inline int sps_free_endpoint(struct sps_pipe *h) +{ + return -EPERM; +} + +static inline int sps_get_config(struct sps_pipe *h, struct sps_connect *config) +{ + return -EPERM; +} + +static inline int sps_alloc_mem(struct sps_pipe *h, enum sps_mem mem, + struct sps_mem_buffer *mem_buffer) +{ + return -EPERM; +} + +static inline int sps_free_mem(struct sps_pipe *h, + struct sps_mem_buffer *mem_buffer) +{ + return -EPERM; +} + +static inline int sps_connect(struct sps_pipe *h, struct sps_connect *connect) +{ + return -EPERM; +} + +static inline int sps_disconnect(struct sps_pipe *h) +{ + return -EPERM; +} + +static inline int sps_register_event(struct sps_pipe *h, + struct sps_register_event *reg) +{ + return -EPERM; +} + +static inline int sps_transfer_one(struct sps_pipe *h, phys_addr_t addr, + u32 size, void *user, u32 flags) +{ + return -EPERM; +} + +static inline int sps_get_event(struct sps_pipe *h, + struct sps_event_notify *event) +{ + return -EPERM; +} + +static inline int sps_get_iovec(struct sps_pipe *h, struct sps_iovec *iovec) +{ + return -EPERM; +} + +static inline int sps_flow_on(struct sps_pipe *h) +{ + return -EPERM; +} + +static inline int sps_flow_off(struct sps_pipe *h, enum sps_flow_off mode) +{ + return -EPERM; +} + +static inline int sps_transfer(struct sps_pipe *h, + struct sps_transfer *transfer) +{ + return -EPERM; +} + +static inline int sps_is_pipe_empty(struct sps_pipe *h, u32 *empty) +{ + return -EPERM; +} + +static inline int sps_device_reset(unsigned long dev) +{ + return -EPERM; +} + +static inline int sps_set_config(struct sps_pipe *h, struct sps_connect *config) +{ + return -EPERM; +} + +static inline int sps_set_owner(struct sps_pipe *h, enum sps_owner owner, + struct sps_satellite *connect) +{ + return -EPERM; +} + +static inline int sps_get_free_count(struct sps_pipe *h, u32 *count) +{ + return -EPERM; +} + +static inline int sps_alloc_dma_chan(const struct sps_alloc_dma_chan *alloc, + struct sps_dma_chan *chan) +{ + return -EPERM; +} + +static inline int sps_free_dma_chan(struct sps_dma_chan *chan) +{ + return -EPERM; +} + +static inline unsigned long sps_dma_get_bam_handle(void) +{ + return 0; +} + +static inline void sps_dma_free_bam_handle(unsigned long h) +{ +} + +static inline int sps_timer_ctrl(struct sps_pipe *h, + struct sps_timer_ctrl *timer_ctrl, + struct sps_timer_result *timer_result) +{ + return -EPERM; +} + +static inline int sps_phy2h(phys_addr_t phys_addr, unsigned long *handle) +{ + return -EPERM; +} + +static inline int sps_setup_bam2bam_fifo(struct sps_mem_buffer *mem_buffer, + u32 addr, u32 size, int use_offset) +{ + return -EPERM; +} + +static inline int sps_get_unused_desc_num(struct sps_pipe *h, u32 *desc_num) +{ + return -EPERM; +} + +static inline int sps_get_bam_debug_info(unsigned long dev, u32 option, + u32 para, u32 tb_sel, u32 desc_sel) +{ + return -EPERM; +} + +static inline int sps_ctrl_bam_dma_clk(bool clk_on) +{ + return -EPERM; +} + +static inline int sps_pipe_reset(unsigned long dev, u32 pipe) +{ + return -EPERM; +} + +static inline int sps_pipe_disable(unsigned long dev, u32 pipe) +{ + return -EPERM; +} + +static inline int sps_pipe_pending_desc(unsigned long dev, u32 pipe, + bool *pending) +{ + return -EPERM; +} + +static inline int sps_bam_process_irq(unsigned long dev) +{ + return -EPERM; +} + +static inline int sps_get_bam_addr(unsigned long dev, phys_addr_t *base, + u32 *size) +{ + return -EPERM; +} + +static inline int sps_pipe_inject_zlt(unsigned long dev, u32 pipe_index) +{ + return -EPERM; +} +#endif + +#endif /* _SPS_H_ */ From 840fdb3ecd1cd155261b83d22238342eeaf6364f Mon Sep 17 00:00:00 2001 From: Chandana Kishori Chiluveru Date: Mon, 10 Oct 2022 06:20:18 -0700 Subject: [PATCH 2/2] ARM: config: msm: Enable BAM config for Pineapple Enable config for BAM driver SPS (Smart Peripheral System) enables the support of all BAMs in the system which provide DMA functionality to various peripherals for Pineapple. Change-Id: Ida34e789704016f6b45af535b015424a46e5d6e2 Signed-off-by: Chandana Kishori Chiluveru --- arch/arm64/configs/vendor/pineapple_GKI.config | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/configs/vendor/pineapple_GKI.config b/arch/arm64/configs/vendor/pineapple_GKI.config index af2fa646ef92..1b037c53d1ae 100644 --- a/arch/arm64/configs/vendor/pineapple_GKI.config +++ b/arch/arm64/configs/vendor/pineapple_GKI.config @@ -75,6 +75,7 @@ CONFIG_POWER_RESET_QCOM_PON=m CONFIG_POWER_RESET_QCOM_REBOOT_REASON=m CONFIG_PWM_QTI_LPG=m CONFIG_QCOM_AOSS_QMP=m +CONFIG_QCOM_BAM_DMA=m CONFIG_QCOM_BWMON=m CONFIG_QCOM_CLK_RPMH=m # CONFIG_QCOM_COINCELL is not set @@ -178,6 +179,8 @@ CONFIG_SM_VIDEOCC_PINEAPPLE=m CONFIG_SPI_MSM_GENI=m CONFIG_SPMI_MSM_PMIC_ARB=m CONFIG_SPMI_QTI_GLINK_DEBUG=m +CONFIG_SPS=m +CONFIG_SPS_SUPPORT_NDP_BAM=y CONFIG_STM=m CONFIG_STM_PROTO_OST=m CONFIG_STM_SOURCE_CONSOLE=m