diff options
Diffstat (limited to 'target/linux/layerscape/patches-5.4/701-net-0007-fsl_qbman-SDK-DPAA-1.x-QBMan-drivers.patch')
-rw-r--r-- | target/linux/layerscape/patches-5.4/701-net-0007-fsl_qbman-SDK-DPAA-1.x-QBMan-drivers.patch | 24880 |
1 files changed, 0 insertions, 24880 deletions
diff --git a/target/linux/layerscape/patches-5.4/701-net-0007-fsl_qbman-SDK-DPAA-1.x-QBMan-drivers.patch b/target/linux/layerscape/patches-5.4/701-net-0007-fsl_qbman-SDK-DPAA-1.x-QBMan-drivers.patch deleted file mode 100644 index b9e7667065..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0007-fsl_qbman-SDK-DPAA-1.x-QBMan-drivers.patch +++ /dev/null @@ -1,24880 +0,0 @@ -From 772438d01bf57bc8939f53c3101a323fc774428f Mon Sep 17 00:00:00 2001 -From: Madalin Bucur <madalin.bucur@nxp.com> -Date: Wed, 10 May 2017 16:30:12 +0300 -Subject: [PATCH] fsl_qbman: SDK DPAA 1.x QBMan drivers - -Signed-off-by: Roy Pledge <roy.pledge@nxp.com> -Signed-off-by: Madalin Bucur <madalin.bucur@nxp.com> ---- - drivers/staging/fsl_qbman/Kconfig | 228 + - drivers/staging/fsl_qbman/Makefile | 28 + - drivers/staging/fsl_qbman/bman_config.c | 720 +++ - drivers/staging/fsl_qbman/bman_debugfs.c | 119 + - drivers/staging/fsl_qbman/bman_driver.c | 559 +++ - drivers/staging/fsl_qbman/bman_high.c | 1145 +++++ - drivers/staging/fsl_qbman/bman_low.h | 565 +++ - drivers/staging/fsl_qbman/bman_private.h | 166 + - drivers/staging/fsl_qbman/bman_test.c | 56 + - drivers/staging/fsl_qbman/bman_test.h | 44 + - drivers/staging/fsl_qbman/bman_test_high.c | 183 + - drivers/staging/fsl_qbman/bman_test_thresh.c | 196 + - drivers/staging/fsl_qbman/dpa_alloc.c | 706 +++ - drivers/staging/fsl_qbman/dpa_sys.h | 259 ++ - drivers/staging/fsl_qbman/dpa_sys_arm.h | 95 + - drivers/staging/fsl_qbman/dpa_sys_arm64.h | 102 + - drivers/staging/fsl_qbman/dpa_sys_ppc32.h | 70 + - drivers/staging/fsl_qbman/dpa_sys_ppc64.h | 79 + - drivers/staging/fsl_qbman/fsl_usdpaa.c | 1984 ++++++++ - drivers/staging/fsl_qbman/fsl_usdpaa_irq.c | 289 ++ - drivers/staging/fsl_qbman/qbman_driver.c | 88 + - drivers/staging/fsl_qbman/qman_config.c | 1224 +++++ - drivers/staging/fsl_qbman/qman_debugfs.c | 1594 +++++++ - drivers/staging/fsl_qbman/qman_driver.c | 961 ++++ - drivers/staging/fsl_qbman/qman_high.c | 5669 +++++++++++++++++++++++ - drivers/staging/fsl_qbman/qman_low.h | 1427 ++++++ - drivers/staging/fsl_qbman/qman_private.h | 398 ++ - drivers/staging/fsl_qbman/qman_test.c | 57 + - drivers/staging/fsl_qbman/qman_test.h | 45 + - drivers/staging/fsl_qbman/qman_test_high.c | 216 + - drivers/staging/fsl_qbman/qman_test_hotpotato.c | 502 ++ - drivers/staging/fsl_qbman/qman_utility.c | 129 + - include/linux/fsl_bman.h | 532 +++ - include/linux/fsl_qman.h | 3888 ++++++++++++++++ - include/linux/fsl_usdpaa.h | 372 ++ - 35 files changed, 24695 insertions(+) - create mode 100644 drivers/staging/fsl_qbman/Kconfig - create mode 100644 drivers/staging/fsl_qbman/Makefile - create mode 100644 drivers/staging/fsl_qbman/bman_config.c - create mode 100644 drivers/staging/fsl_qbman/bman_debugfs.c - create mode 100644 drivers/staging/fsl_qbman/bman_driver.c - create mode 100644 drivers/staging/fsl_qbman/bman_high.c - create mode 100644 drivers/staging/fsl_qbman/bman_low.h - create mode 100644 drivers/staging/fsl_qbman/bman_private.h - create mode 100644 drivers/staging/fsl_qbman/bman_test.c - create mode 100644 drivers/staging/fsl_qbman/bman_test.h - create mode 100644 drivers/staging/fsl_qbman/bman_test_high.c - create mode 100644 drivers/staging/fsl_qbman/bman_test_thresh.c - create mode 100644 drivers/staging/fsl_qbman/dpa_alloc.c - create mode 100644 drivers/staging/fsl_qbman/dpa_sys.h - create mode 100644 drivers/staging/fsl_qbman/dpa_sys_arm.h - create mode 100644 drivers/staging/fsl_qbman/dpa_sys_arm64.h - create mode 100644 drivers/staging/fsl_qbman/dpa_sys_ppc32.h - create mode 100644 drivers/staging/fsl_qbman/dpa_sys_ppc64.h - create mode 100644 drivers/staging/fsl_qbman/fsl_usdpaa.c - create mode 100644 drivers/staging/fsl_qbman/fsl_usdpaa_irq.c - create mode 100644 drivers/staging/fsl_qbman/qbman_driver.c - create mode 100644 drivers/staging/fsl_qbman/qman_config.c - create mode 100644 drivers/staging/fsl_qbman/qman_debugfs.c - create mode 100644 drivers/staging/fsl_qbman/qman_driver.c - create mode 100644 drivers/staging/fsl_qbman/qman_high.c - create mode 100644 drivers/staging/fsl_qbman/qman_low.h - create mode 100644 drivers/staging/fsl_qbman/qman_private.h - create mode 100644 drivers/staging/fsl_qbman/qman_test.c - create mode 100644 drivers/staging/fsl_qbman/qman_test.h - create mode 100644 drivers/staging/fsl_qbman/qman_test_high.c - create mode 100644 drivers/staging/fsl_qbman/qman_test_hotpotato.c - create mode 100644 drivers/staging/fsl_qbman/qman_utility.c - create mode 100644 include/linux/fsl_bman.h - create mode 100644 include/linux/fsl_qman.h - create mode 100644 include/linux/fsl_usdpaa.h - ---- /dev/null -+++ b/drivers/staging/fsl_qbman/Kconfig -@@ -0,0 +1,228 @@ -+config FSL_SDK_DPA -+ bool "Freescale Datapath Queue and Buffer management" -+ depends on !FSL_DPAA -+ select FSL_QMAN_FQ_LOOKUP if PPC64 -+ select FSL_QMAN_FQ_LOOKUP if ARM64 -+ -+ -+menu "Freescale Datapath QMan/BMan options" -+ depends on FSL_SDK_DPA -+ -+config FSL_DPA_CHECKING -+ bool "additional driver checking" -+ default n -+ ---help--- -+ Compiles in additional checks to sanity-check the drivers and any -+ use of it by other code. Not recommended for performance. -+ -+config FSL_DPA_CAN_WAIT -+ bool -+ default y -+ -+config FSL_DPA_CAN_WAIT_SYNC -+ bool -+ default y -+ -+config FSL_DPA_PIRQ_FAST -+ bool -+ default y -+ -+config FSL_DPA_PIRQ_SLOW -+ bool -+ default y -+ -+config FSL_DPA_PORTAL_SHARE -+ bool -+ default y -+ -+config FSL_SDK_BMAN -+ bool "Freescale Buffer Manager (BMan) support" -+ default y -+ -+if FSL_SDK_BMAN -+ -+config FSL_BMAN_CONFIG -+ bool "BMan device management" -+ default y -+ ---help--- -+ If this linux image is running natively, you need this option. If this -+ linux image is running as a guest OS under the hypervisor, only one -+ guest OS ("the control plane") needs this option. -+ -+config FSL_BMAN_TEST -+ tristate "BMan self-tests" -+ default n -+ ---help--- -+ This option compiles self-test code for BMan. -+ -+config FSL_BMAN_TEST_HIGH -+ bool "BMan high-level self-test" -+ depends on FSL_BMAN_TEST -+ default y -+ ---help--- -+ This requires the presence of cpu-affine portals, and performs -+ high-level API testing with them (whichever portal(s) are affine to -+ the cpu(s) the test executes on). -+ -+config FSL_BMAN_TEST_THRESH -+ bool "BMan threshold test" -+ depends on FSL_BMAN_TEST -+ default y -+ ---help--- -+ Multi-threaded (SMP) test of BMan pool depletion. A pool is seeded -+ before multiple threads (one per cpu) create pool objects to track -+ depletion state changes. The pool is then drained to empty by a -+ "drainer" thread, and the other threads that they observe exactly -+ the depletion state changes that are expected. -+ -+config FSL_BMAN_DEBUGFS -+ tristate "BMan debugfs interface" -+ depends on DEBUG_FS -+ default y -+ ---help--- -+ This option compiles debugfs code for BMan. -+ -+endif # FSL_SDK_BMAN -+ -+config FSL_SDK_QMAN -+ bool "Freescale Queue Manager (QMan) support" -+ default y -+ -+if FSL_SDK_QMAN -+ -+config FSL_QMAN_POLL_LIMIT -+ int -+ default 32 -+ -+config FSL_QMAN_CONFIG -+ bool "QMan device management" -+ default y -+ ---help--- -+ If this linux image is running natively, you need this option. If this -+ linux image is running as a guest OS under the hypervisor, only one -+ guest OS ("the control plane") needs this option. -+ -+config FSL_QMAN_TEST -+ tristate "QMan self-tests" -+ default n -+ ---help--- -+ This option compiles self-test code for QMan. -+ -+config FSL_QMAN_TEST_STASH_POTATO -+ bool "QMan 'hot potato' data-stashing self-test" -+ depends on FSL_QMAN_TEST -+ default y -+ ---help--- -+ This performs a "hot potato" style test enqueuing/dequeuing a frame -+ across a series of FQs scheduled to different portals (and cpus), with -+ DQRR, data and context stashing always on. -+ -+config FSL_QMAN_TEST_HIGH -+ bool "QMan high-level self-test" -+ depends on FSL_QMAN_TEST -+ default y -+ ---help--- -+ This requires the presence of cpu-affine portals, and performs -+ high-level API testing with them (whichever portal(s) are affine to -+ the cpu(s) the test executes on). -+ -+config FSL_QMAN_DEBUGFS -+ tristate "QMan debugfs interface" -+ depends on DEBUG_FS -+ default y -+ ---help--- -+ This option compiles debugfs code for QMan. -+ -+# H/w settings that can be hard-coded for now. -+config FSL_QMAN_FQD_SZ -+ int "size of Frame Queue Descriptor region" -+ default 10 -+ ---help--- -+ This is the size of the FQD region defined as: PAGE_SIZE * (2^value) -+ ex: 10 => PAGE_SIZE * (2^10) -+ Note: Default device-trees now require minimum Kconfig setting of 10. -+ -+config FSL_QMAN_PFDR_SZ -+ int "size of the PFDR pool" -+ default 13 -+ ---help--- -+ This is the size of the PFDR pool defined as: PAGE_SIZE * (2^value) -+ ex: 13 => PAGE_SIZE * (2^13) -+ -+# Corenet initiator settings. Stash request queues are 4-deep to match cores' -+# ability to snart. Stash priority is 3, other priorities are 2. -+config FSL_QMAN_CI_SCHED_CFG_SRCCIV -+ int -+ depends on FSL_QMAN_CONFIG -+ default 4 -+config FSL_QMAN_CI_SCHED_CFG_SRQ_W -+ int -+ depends on FSL_QMAN_CONFIG -+ default 3 -+config FSL_QMAN_CI_SCHED_CFG_RW_W -+ int -+ depends on FSL_QMAN_CONFIG -+ default 2 -+config FSL_QMAN_CI_SCHED_CFG_BMAN_W -+ int -+ depends on FSL_QMAN_CONFIG -+ default 2 -+ -+# portal interrupt settings -+config FSL_QMAN_PIRQ_DQRR_ITHRESH -+ int -+ default 12 -+config FSL_QMAN_PIRQ_MR_ITHRESH -+ int -+ default 4 -+config FSL_QMAN_PIRQ_IPERIOD -+ int -+ default 100 -+ -+# 64 bit kernel support -+config FSL_QMAN_FQ_LOOKUP -+ bool -+ default n -+ -+config QMAN_CEETM_UPDATE_PERIOD -+ int "Token update period for shaping, in nanoseconds" -+ default 1000 -+ ---help--- -+ Traffic shaping works by performing token calculations (using -+ credits) on shaper instances periodically. This update period -+ sets the granularity for how often those token rate credit -+ updates are performed, and thus determines the accuracy and -+ range of traffic rates that can be configured by users. The -+ reference manual recommends a 1 microsecond period as providing -+ a good balance between granularity and range. -+ -+ Unless you know what you are doing, leave this value at its default. -+ -+config FSL_QMAN_INIT_TIMEOUT -+ int "timeout for qman init stage, in seconds" -+ default 10 -+ ---help--- -+ The timeout setting to quit the initialization loop for non-control -+ partition in case the control partition fails to boot-up. -+ -+endif # FSL_SDK_QMAN -+ -+config FSL_USDPAA -+ bool "Freescale USDPAA process driver" -+ depends on FSL_SDK_DPA -+ default y -+ ---help--- -+ This driver provides user-space access to kernel-managed -+ resource interfaces for USDPAA applications, on the assumption -+ that each process will open this device once. Specifically, this -+ device exposes functionality that would be awkward if exposed -+ via the portal devices - ie. this device exposes functionality -+ that is inherently process-wide rather than portal-specific. -+ This device is necessary for obtaining access to DMA memory and -+ for allocation of Qman and Bman resources. In short, if you wish -+ to use USDPAA applications, you need this. -+ -+ If unsure, say Y. -+ -+ -+endmenu ---- /dev/null -+++ b/drivers/staging/fsl_qbman/Makefile -@@ -0,0 +1,28 @@ -+subdir-ccflags-y := -Werror -+ -+# Common -+obj-$(CONFIG_FSL_SDK_DPA) += dpa_alloc.o -+obj-$(CONFIG_FSL_SDK_DPA) += qbman_driver.o -+ -+# Bman -+obj-$(CONFIG_FSL_SDK_BMAN) += bman_high.o -+obj-$(CONFIG_FSL_BMAN_CONFIG) += bman_config.o bman_driver.o -+obj-$(CONFIG_FSL_BMAN_TEST) += bman_tester.o -+obj-$(CONFIG_FSL_BMAN_DEBUGFS) += bman_debugfs_interface.o -+bman_tester-y = bman_test.o -+bman_tester-$(CONFIG_FSL_BMAN_TEST_HIGH) += bman_test_high.o -+bman_tester-$(CONFIG_FSL_BMAN_TEST_THRESH) += bman_test_thresh.o -+bman_debugfs_interface-y = bman_debugfs.o -+ -+# Qman -+obj-$(CONFIG_FSL_SDK_QMAN) += qman_high.o qman_utility.o -+obj-$(CONFIG_FSL_QMAN_CONFIG) += qman_config.o qman_driver.o -+obj-$(CONFIG_FSL_QMAN_TEST) += qman_tester.o -+qman_tester-y = qman_test.o -+qman_tester-$(CONFIG_FSL_QMAN_TEST_STASH_POTATO) += qman_test_hotpotato.o -+qman_tester-$(CONFIG_FSL_QMAN_TEST_HIGH) += qman_test_high.o -+obj-$(CONFIG_FSL_QMAN_DEBUGFS) += qman_debugfs_interface.o -+qman_debugfs_interface-y = qman_debugfs.o -+ -+# USDPAA -+obj-$(CONFIG_FSL_USDPAA) += fsl_usdpaa.o fsl_usdpaa_irq.o ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_config.c -@@ -0,0 +1,720 @@ -+/* Copyright (c) 2009-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include <asm/cacheflush.h> -+#include "bman_private.h" -+#include <linux/of_reserved_mem.h> -+ -+/* Last updated for v00.79 of the BG */ -+ -+struct bman; -+ -+/* Register offsets */ -+#define REG_POOL_SWDET(n) (0x0000 + ((n) * 0x04)) -+#define REG_POOL_HWDET(n) (0x0100 + ((n) * 0x04)) -+#define REG_POOL_SWDXT(n) (0x0200 + ((n) * 0x04)) -+#define REG_POOL_HWDXT(n) (0x0300 + ((n) * 0x04)) -+#define REG_POOL_CONTENT(n) (0x0600 + ((n) * 0x04)) -+#define REG_FBPR_FPC 0x0800 -+#define REG_STATE_IDLE 0x960 -+#define REG_STATE_STOP 0x964 -+#define REG_ECSR 0x0a00 -+#define REG_ECIR 0x0a04 -+#define REG_EADR 0x0a08 -+#define REG_EDATA(n) (0x0a10 + ((n) * 0x04)) -+#define REG_SBEC(n) (0x0a80 + ((n) * 0x04)) -+#define REG_IP_REV_1 0x0bf8 -+#define REG_IP_REV_2 0x0bfc -+#define REG_FBPR_BARE 0x0c00 -+#define REG_FBPR_BAR 0x0c04 -+#define REG_FBPR_AR 0x0c10 -+#define REG_SRCIDR 0x0d04 -+#define REG_LIODNR 0x0d08 -+#define REG_ERR_ISR 0x0e00 /* + "enum bm_isr_reg" */ -+ -+/* Used by all error interrupt registers except 'inhibit' */ -+#define BM_EIRQ_IVCI 0x00000010 /* Invalid Command Verb */ -+#define BM_EIRQ_FLWI 0x00000008 /* FBPR Low Watermark */ -+#define BM_EIRQ_MBEI 0x00000004 /* Multi-bit ECC Error */ -+#define BM_EIRQ_SBEI 0x00000002 /* Single-bit ECC Error */ -+#define BM_EIRQ_BSCN 0x00000001 /* pool State Change Notification */ -+ -+/* BMAN_ECIR valid error bit */ -+#define PORTAL_ECSR_ERR (BM_EIRQ_IVCI) -+ -+union bman_ecir { -+ u32 ecir_raw; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 __reserved1:4; -+ u32 portal_num:4; -+ u32 __reserved2:12; -+ u32 numb:4; -+ u32 __reserved3:2; -+ u32 pid:6; -+#else -+ u32 pid:6; -+ u32 __reserved3:2; -+ u32 numb:4; -+ u32 __reserved2:12; -+ u32 portal_num:4; -+ u32 __reserved1:4; -+#endif -+ } __packed info; -+}; -+ -+union bman_eadr { -+ u32 eadr_raw; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 __reserved1:5; -+ u32 memid:3; -+ u32 __reserved2:14; -+ u32 eadr:10; -+#else -+ u32 eadr:10; -+ u32 __reserved2:14; -+ u32 memid:3; -+ u32 __reserved1:5; -+#endif -+ } __packed info; -+}; -+ -+struct bman_hwerr_txt { -+ u32 mask; -+ const char *txt; -+}; -+ -+#define BMAN_HWE_TXT(a, b) { .mask = BM_EIRQ_##a, .txt = b } -+ -+static const struct bman_hwerr_txt bman_hwerr_txts[] = { -+ BMAN_HWE_TXT(IVCI, "Invalid Command Verb"), -+ BMAN_HWE_TXT(FLWI, "FBPR Low Watermark"), -+ BMAN_HWE_TXT(MBEI, "Multi-bit ECC Error"), -+ BMAN_HWE_TXT(SBEI, "Single-bit ECC Error"), -+ BMAN_HWE_TXT(BSCN, "Pool State Change Notification"), -+}; -+#define BMAN_HWE_COUNT (sizeof(bman_hwerr_txts)/sizeof(struct bman_hwerr_txt)) -+ -+struct bman_error_info_mdata { -+ u16 addr_mask; -+ u16 bits; -+ const char *txt; -+}; -+ -+#define BMAN_ERR_MDATA(a, b, c) { .addr_mask = a, .bits = b, .txt = c} -+static const struct bman_error_info_mdata error_mdata[] = { -+ BMAN_ERR_MDATA(0x03FF, 192, "Stockpile memory"), -+ BMAN_ERR_MDATA(0x00FF, 256, "SW portal ring memory port 1"), -+ BMAN_ERR_MDATA(0x00FF, 256, "SW portal ring memory port 2"), -+}; -+#define BMAN_ERR_MDATA_COUNT \ -+ (sizeof(error_mdata)/sizeof(struct bman_error_info_mdata)) -+ -+/* Add this in Kconfig */ -+#define BMAN_ERRS_TO_UNENABLE (BM_EIRQ_FLWI) -+ -+/** -+ * bm_err_isr_<reg>_<verb> - Manipulate global interrupt registers -+ * @v: for accessors that write values, this is the 32-bit value -+ * -+ * Manipulates BMAN_ERR_ISR, BMAN_ERR_IER, BMAN_ERR_ISDR, BMAN_ERR_IIR. All -+ * manipulations except bm_err_isr_[un]inhibit() use 32-bit masks composed of -+ * the BM_EIRQ_*** definitions. Note that "bm_err_isr_enable_write" means -+ * "write the enable register" rather than "enable the write register"! -+ */ -+#define bm_err_isr_status_read(bm) \ -+ __bm_err_isr_read(bm, bm_isr_status) -+#define bm_err_isr_status_clear(bm, m) \ -+ __bm_err_isr_write(bm, bm_isr_status, m) -+#define bm_err_isr_enable_read(bm) \ -+ __bm_err_isr_read(bm, bm_isr_enable) -+#define bm_err_isr_enable_write(bm, v) \ -+ __bm_err_isr_write(bm, bm_isr_enable, v) -+#define bm_err_isr_disable_read(bm) \ -+ __bm_err_isr_read(bm, bm_isr_disable) -+#define bm_err_isr_disable_write(bm, v) \ -+ __bm_err_isr_write(bm, bm_isr_disable, v) -+#define bm_err_isr_inhibit(bm) \ -+ __bm_err_isr_write(bm, bm_isr_inhibit, 1) -+#define bm_err_isr_uninhibit(bm) \ -+ __bm_err_isr_write(bm, bm_isr_inhibit, 0) -+ -+/* -+ * TODO: unimplemented registers -+ * -+ * BMAN_POOLk_SDCNT, BMAN_POOLk_HDCNT, BMAN_FULT, -+ * BMAN_VLDPL, BMAN_EECC, BMAN_SBET, BMAN_EINJ -+ */ -+ -+/* Encapsulate "struct bman *" as a cast of the register space address. */ -+ -+static struct bman *bm_create(void *regs) -+{ -+ return (struct bman *)regs; -+} -+ -+static inline u32 __bm_in(struct bman *bm, u32 offset) -+{ -+ return in_be32((void *)bm + offset); -+} -+static inline void __bm_out(struct bman *bm, u32 offset, u32 val) -+{ -+ out_be32((void *)bm + offset, val); -+} -+#define bm_in(reg) __bm_in(bm, REG_##reg) -+#define bm_out(reg, val) __bm_out(bm, REG_##reg, val) -+ -+static u32 __bm_err_isr_read(struct bman *bm, enum bm_isr_reg n) -+{ -+ return __bm_in(bm, REG_ERR_ISR + (n << 2)); -+} -+ -+static void __bm_err_isr_write(struct bman *bm, enum bm_isr_reg n, u32 val) -+{ -+ __bm_out(bm, REG_ERR_ISR + (n << 2), val); -+} -+ -+static void bm_get_version(struct bman *bm, u16 *id, u8 *major, u8 *minor) -+{ -+ u32 v = bm_in(IP_REV_1); -+ *id = (v >> 16); -+ *major = (v >> 8) & 0xff; -+ *minor = v & 0xff; -+} -+ -+static u32 __generate_thresh(u32 val, int roundup) -+{ -+ u32 e = 0; /* co-efficient, exponent */ -+ int oddbit = 0; -+ while (val > 0xff) { -+ oddbit = val & 1; -+ val >>= 1; -+ e++; -+ if (roundup && oddbit) -+ val++; -+ } -+ DPA_ASSERT(e < 0x10); -+ return val | (e << 8); -+} -+ -+static void bm_set_pool(struct bman *bm, u8 pool, u32 swdet, u32 swdxt, -+ u32 hwdet, u32 hwdxt) -+{ -+ DPA_ASSERT(pool < bman_pool_max); -+ bm_out(POOL_SWDET(pool), __generate_thresh(swdet, 0)); -+ bm_out(POOL_SWDXT(pool), __generate_thresh(swdxt, 1)); -+ bm_out(POOL_HWDET(pool), __generate_thresh(hwdet, 0)); -+ bm_out(POOL_HWDXT(pool), __generate_thresh(hwdxt, 1)); -+} -+ -+static void bm_set_memory(struct bman *bm, u64 ba, int prio, u32 size) -+{ -+ u32 exp = ilog2(size); -+ /* choke if size isn't within range */ -+ DPA_ASSERT((size >= 4096) && (size <= 1073741824) && -+ is_power_of_2(size)); -+ /* choke if '[e]ba' has lower-alignment than 'size' */ -+ DPA_ASSERT(!(ba & (size - 1))); -+ bm_out(FBPR_BARE, upper_32_bits(ba)); -+ bm_out(FBPR_BAR, lower_32_bits(ba)); -+ bm_out(FBPR_AR, (prio ? 0x40000000 : 0) | (exp - 1)); -+} -+ -+/*****************/ -+/* Config driver */ -+/*****************/ -+ -+/* TODO: Kconfig these? */ -+#define DEFAULT_FBPR_SZ (PAGE_SIZE << 12) -+ -+/* We support only one of these. */ -+static struct bman *bm; -+static struct device_node *bm_node; -+ -+/* And this state belongs to 'bm'. It is set during fsl_bman_init(), but used -+ * during bman_init_ccsr(). */ -+static dma_addr_t fbpr_a; -+static size_t fbpr_sz = DEFAULT_FBPR_SZ; -+ -+static int bman_fbpr(struct reserved_mem *rmem) -+{ -+ fbpr_a = rmem->base; -+ fbpr_sz = rmem->size; -+ -+ WARN_ON(!(fbpr_a && fbpr_sz)); -+ -+ return 0; -+} -+RESERVEDMEM_OF_DECLARE(bman_fbpr, "fsl,bman-fbpr", bman_fbpr); -+ -+static int __init fsl_bman_init(struct device_node *node) -+{ -+ struct resource res; -+ u32 __iomem *regs; -+ const char *s; -+ int ret, standby = 0; -+ u16 id; -+ u8 major, minor; -+ -+ ret = of_address_to_resource(node, 0, &res); -+ if (ret) { -+ pr_err("Can't get %s property 'reg'\n", -+ node->full_name); -+ return ret; -+ } -+ s = of_get_property(node, "fsl,hv-claimable", &ret); -+ if (s && !strcmp(s, "standby")) -+ standby = 1; -+ /* Global configuration */ -+ regs = ioremap(res.start, res.end - res.start + 1); -+ bm = bm_create(regs); -+ BUG_ON(!bm); -+ bm_node = node; -+ bm_get_version(bm, &id, &major, &minor); -+ pr_info("Bman ver:%04x,%02x,%02x\n", id, major, minor); -+ if ((major == 1) && (minor == 0)) { -+ bman_ip_rev = BMAN_REV10; -+ bman_pool_max = 64; -+ } else if ((major == 2) && (minor == 0)) { -+ bman_ip_rev = BMAN_REV20; -+ bman_pool_max = 8; -+ } else if ((major == 2) && (minor == 1)) { -+ bman_ip_rev = BMAN_REV21; -+ bman_pool_max = 64; -+ } else { -+ pr_warn("unknown Bman version, default to rev1.0\n"); -+ } -+ -+ if (standby) { -+ pr_info(" -> in standby mode\n"); -+ return 0; -+ } -+ return 0; -+} -+ -+int bman_have_ccsr(void) -+{ -+ return bm ? 1 : 0; -+} -+ -+int bm_pool_set(u32 bpid, const u32 *thresholds) -+{ -+ if (!bm) -+ return -ENODEV; -+ bm_set_pool(bm, bpid, thresholds[0], -+ thresholds[1], thresholds[2], -+ thresholds[3]); -+ return 0; -+} -+EXPORT_SYMBOL(bm_pool_set); -+ -+__init int bman_init_early(void) -+{ -+ struct device_node *dn; -+ int ret; -+ -+ for_each_compatible_node(dn, NULL, "fsl,bman") { -+ if (bm) -+ pr_err("%s: only one 'fsl,bman' allowed\n", -+ dn->full_name); -+ else { -+ if (!of_device_is_available(dn)) -+ continue; -+ -+ ret = fsl_bman_init(dn); -+ BUG_ON(ret); -+ } -+ } -+ return 0; -+} -+postcore_initcall_sync(bman_init_early); -+ -+ -+static void log_edata_bits(u32 bit_count) -+{ -+ u32 i, j, mask = 0xffffffff; -+ -+ pr_warn("Bman ErrInt, EDATA:\n"); -+ i = bit_count/32; -+ if (bit_count%32) { -+ i++; -+ mask = ~(mask << bit_count%32); -+ } -+ j = 16-i; -+ pr_warn(" 0x%08x\n", bm_in(EDATA(j)) & mask); -+ j++; -+ for (; j < 16; j++) -+ pr_warn(" 0x%08x\n", bm_in(EDATA(j))); -+} -+ -+static void log_additional_error_info(u32 isr_val, u32 ecsr_val) -+{ -+ union bman_ecir ecir_val; -+ union bman_eadr eadr_val; -+ -+ ecir_val.ecir_raw = bm_in(ECIR); -+ /* Is portal info valid */ -+ if (ecsr_val & PORTAL_ECSR_ERR) { -+ pr_warn("Bman ErrInt: SWP id %d, numb %d, pid %d\n", -+ ecir_val.info.portal_num, ecir_val.info.numb, -+ ecir_val.info.pid); -+ } -+ if (ecsr_val & (BM_EIRQ_SBEI|BM_EIRQ_MBEI)) { -+ eadr_val.eadr_raw = bm_in(EADR); -+ pr_warn("Bman ErrInt: EADR Memory: %s, 0x%x\n", -+ error_mdata[eadr_val.info.memid].txt, -+ error_mdata[eadr_val.info.memid].addr_mask -+ & eadr_val.info.eadr); -+ log_edata_bits(error_mdata[eadr_val.info.memid].bits); -+ } -+} -+ -+/* Bman interrupt handler */ -+static irqreturn_t bman_isr(int irq, void *ptr) -+{ -+ u32 isr_val, ier_val, ecsr_val, isr_mask, i; -+ -+ ier_val = bm_err_isr_enable_read(bm); -+ isr_val = bm_err_isr_status_read(bm); -+ ecsr_val = bm_in(ECSR); -+ isr_mask = isr_val & ier_val; -+ -+ if (!isr_mask) -+ return IRQ_NONE; -+ for (i = 0; i < BMAN_HWE_COUNT; i++) { -+ if (bman_hwerr_txts[i].mask & isr_mask) { -+ pr_warn("Bman ErrInt: %s\n", bman_hwerr_txts[i].txt); -+ if (bman_hwerr_txts[i].mask & ecsr_val) { -+ log_additional_error_info(isr_mask, ecsr_val); -+ /* Re-arm error capture registers */ -+ bm_out(ECSR, ecsr_val); -+ } -+ if (bman_hwerr_txts[i].mask & BMAN_ERRS_TO_UNENABLE) { -+ pr_devel("Bman un-enabling error 0x%x\n", -+ bman_hwerr_txts[i].mask); -+ ier_val &= ~bman_hwerr_txts[i].mask; -+ bm_err_isr_enable_write(bm, ier_val); -+ } -+ } -+ } -+ bm_err_isr_status_clear(bm, isr_val); -+ return IRQ_HANDLED; -+} -+ -+static int __bind_irq(void) -+{ -+ int ret, err_irq; -+ -+ err_irq = of_irq_to_resource(bm_node, 0, NULL); -+ if (err_irq == 0) { -+ pr_info("Can't get %s property '%s'\n", bm_node->full_name, -+ "interrupts"); -+ return -ENODEV; -+ } -+ ret = request_irq(err_irq, bman_isr, IRQF_SHARED, "bman-err", bm_node); -+ if (ret) { -+ pr_err("request_irq() failed %d for '%s'\n", ret, -+ bm_node->full_name); -+ return -ENODEV; -+ } -+ /* Disable Buffer Pool State Change */ -+ bm_err_isr_disable_write(bm, BM_EIRQ_BSCN); -+ /* Write-to-clear any stale bits, (eg. starvation being asserted prior -+ * to resource allocation during driver init). */ -+ bm_err_isr_status_clear(bm, 0xffffffff); -+ /* Enable Error Interrupts */ -+ bm_err_isr_enable_write(bm, 0xffffffff); -+ return 0; -+} -+ -+int bman_init_ccsr(struct device_node *node) -+{ -+ int ret; -+ if (!bman_have_ccsr()) -+ return 0; -+ if (node != bm_node) -+ return -EINVAL; -+ /* FBPR memory */ -+ bm_set_memory(bm, fbpr_a, 0, fbpr_sz); -+ pr_info("bman-fbpr addr %pad size 0x%zx\n", &fbpr_a, fbpr_sz); -+ -+ ret = __bind_irq(); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+u32 bm_pool_free_buffers(u32 bpid) -+{ -+ return bm_in(POOL_CONTENT(bpid)); -+} -+ -+#ifdef CONFIG_SYSFS -+ -+#define DRV_NAME "fsl-bman" -+#define SBEC_MAX_ID 1 -+#define SBEC_MIN_ID 0 -+ -+static ssize_t show_fbpr_fpc(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "%u\n", bm_in(FBPR_FPC)); -+}; -+ -+static ssize_t show_pool_count(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ u32 data; -+ int i; -+ -+ if (!sscanf(dev_attr->attr.name, "%d", &i) || (i >= bman_pool_max)) -+ return -EINVAL; -+ data = bm_in(POOL_CONTENT(i)); -+ return snprintf(buf, PAGE_SIZE, "%d\n", data); -+}; -+ -+static ssize_t show_err_isr(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "0x%08x\n", bm_in(ERR_ISR)); -+}; -+ -+static ssize_t show_sbec(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ int i; -+ -+ if (!sscanf(dev_attr->attr.name, "sbec_%d", &i)) -+ return -EINVAL; -+ if (i < SBEC_MIN_ID || i > SBEC_MAX_ID) -+ return -EINVAL; -+ return snprintf(buf, PAGE_SIZE, "%u\n", bm_in(SBEC(i))); -+}; -+ -+static DEVICE_ATTR(err_isr, S_IRUSR, show_err_isr, NULL); -+static DEVICE_ATTR(fbpr_fpc, S_IRUSR, show_fbpr_fpc, NULL); -+ -+/* Didn't use DEVICE_ATTR as 64 of this would be required. -+ * Initialize them when needed. */ -+static char *name_attrs_pool_count; /* "xx" + null-terminator */ -+static struct device_attribute *dev_attr_buffer_pool_count; -+ -+static DEVICE_ATTR(sbec_0, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_1, S_IRUSR, show_sbec, NULL); -+ -+static struct attribute *bman_dev_attributes[] = { -+ &dev_attr_fbpr_fpc.attr, -+ &dev_attr_err_isr.attr, -+ NULL -+}; -+ -+static struct attribute *bman_dev_ecr_attributes[] = { -+ &dev_attr_sbec_0.attr, -+ &dev_attr_sbec_1.attr, -+ NULL -+}; -+ -+static struct attribute **bman_dev_pool_count_attributes; -+ -+ -+/* root level */ -+static const struct attribute_group bman_dev_attr_grp = { -+ .name = NULL, -+ .attrs = bman_dev_attributes -+}; -+static const struct attribute_group bman_dev_ecr_grp = { -+ .name = "error_capture", -+ .attrs = bman_dev_ecr_attributes -+}; -+static struct attribute_group bman_dev_pool_countent_grp = { -+ .name = "pool_count", -+}; -+ -+static int of_fsl_bman_remove(struct platform_device *ofdev) -+{ -+ sysfs_remove_group(&ofdev->dev.kobj, &bman_dev_attr_grp); -+ return 0; -+}; -+ -+static int of_fsl_bman_probe(struct platform_device *ofdev) -+{ -+ int ret, i; -+ -+ ret = sysfs_create_group(&ofdev->dev.kobj, &bman_dev_attr_grp); -+ if (ret) -+ goto done; -+ ret = sysfs_create_group(&ofdev->dev.kobj, &bman_dev_ecr_grp); -+ if (ret) -+ goto del_group_0; -+ -+ name_attrs_pool_count = kmalloc(sizeof(char) * bman_pool_max * 3, -+ GFP_KERNEL); -+ if (!name_attrs_pool_count) { -+ pr_err("Can't alloc name_attrs_pool_count\n"); -+ goto del_group_1; -+ } -+ -+ dev_attr_buffer_pool_count = kmalloc(sizeof(struct device_attribute) * -+ bman_pool_max, GFP_KERNEL); -+ if (!dev_attr_buffer_pool_count) { -+ pr_err("Can't alloc dev_attr-buffer_pool_count\n"); -+ goto del_group_2; -+ } -+ -+ bman_dev_pool_count_attributes = kmalloc(sizeof(struct attribute *) * -+ (bman_pool_max + 1), GFP_KERNEL); -+ if (!bman_dev_pool_count_attributes) { -+ pr_err("can't alloc bman_dev_pool_count_attributes\n"); -+ goto del_group_3; -+ } -+ -+ for (i = 0; i < bman_pool_max; i++) { -+ ret = scnprintf((name_attrs_pool_count + i * 3), 3, "%d", i); -+ if (!ret) -+ goto del_group_4; -+ dev_attr_buffer_pool_count[i].attr.name = -+ (name_attrs_pool_count + i * 3); -+ dev_attr_buffer_pool_count[i].attr.mode = S_IRUSR; -+ dev_attr_buffer_pool_count[i].show = show_pool_count; -+ bman_dev_pool_count_attributes[i] = -+ &dev_attr_buffer_pool_count[i].attr; -+ sysfs_attr_init(bman_dev_pool_count_attributes[i]); -+ } -+ bman_dev_pool_count_attributes[bman_pool_max] = NULL; -+ -+ bman_dev_pool_countent_grp.attrs = bman_dev_pool_count_attributes; -+ -+ ret = sysfs_create_group(&ofdev->dev.kobj, &bman_dev_pool_countent_grp); -+ if (ret) -+ goto del_group_4; -+ -+ goto done; -+ -+del_group_4: -+ kfree(bman_dev_pool_count_attributes); -+del_group_3: -+ kfree(dev_attr_buffer_pool_count); -+del_group_2: -+ kfree(name_attrs_pool_count); -+del_group_1: -+ sysfs_remove_group(&ofdev->dev.kobj, &bman_dev_ecr_grp); -+del_group_0: -+ sysfs_remove_group(&ofdev->dev.kobj, &bman_dev_attr_grp); -+done: -+ if (ret) -+ dev_err(&ofdev->dev, -+ "Cannot create dev attributes ret=%d\n", ret); -+ return ret; -+}; -+ -+static struct of_device_id of_fsl_bman_ids[] = { -+ { -+ .compatible = "fsl,bman", -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, of_fsl_bman_ids); -+ -+#ifdef CONFIG_SUSPEND -+static u32 saved_isdr; -+ -+static int bman_pm_suspend_noirq(struct device *dev) -+{ -+ uint32_t idle_state; -+ -+ suspend_unused_bportal(); -+ /* save isdr, disable all, clear isr */ -+ saved_isdr = bm_err_isr_disable_read(bm); -+ bm_err_isr_disable_write(bm, 0xffffffff); -+ bm_err_isr_status_clear(bm, 0xffffffff); -+ -+ if (bman_ip_rev < BMAN_REV21) { -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Bman version doesn't have STATE_IDLE\n"); -+#endif -+ return 0; -+ } -+ idle_state = bm_in(STATE_IDLE); -+ if (!(idle_state & 0x1)) { -+ pr_err("Bman not idle 0x%x aborting\n", idle_state); -+ bm_err_isr_disable_write(bm, saved_isdr); -+ resume_unused_bportal(); -+ return -EBUSY; -+ } -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Bman suspend code, IDLE_STAT = 0x%x\n", idle_state); -+#endif -+ return 0; -+} -+ -+static int bman_pm_resume_noirq(struct device *dev) -+{ -+ /* restore isdr */ -+ bm_err_isr_disable_write(bm, saved_isdr); -+ resume_unused_bportal(); -+ return 0; -+} -+#else -+#define bman_pm_suspend_noirq NULL -+#define bman_pm_resume_noirq NULL -+#endif -+ -+static const struct dev_pm_ops bman_pm_ops = { -+ .suspend_noirq = bman_pm_suspend_noirq, -+ .resume_noirq = bman_pm_resume_noirq, -+}; -+ -+static struct platform_driver of_fsl_bman_driver = { -+ .driver = { -+ .owner = THIS_MODULE, -+ .name = DRV_NAME, -+ .of_match_table = of_fsl_bman_ids, -+ .pm = &bman_pm_ops, -+ }, -+ .probe = of_fsl_bman_probe, -+ .remove = of_fsl_bman_remove, -+}; -+ -+static int bman_ctrl_init(void) -+{ -+ return platform_driver_register(&of_fsl_bman_driver); -+} -+ -+static void bman_ctrl_exit(void) -+{ -+ platform_driver_unregister(&of_fsl_bman_driver); -+} -+ -+module_init(bman_ctrl_init); -+module_exit(bman_ctrl_exit); -+ -+#endif /* CONFIG_SYSFS */ ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_debugfs.c -@@ -0,0 +1,119 @@ -+/* Copyright 2010-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+#include <linux/module.h> -+#include <linux/fsl_bman.h> -+#include <linux/debugfs.h> -+#include <linux/seq_file.h> -+#include <linux/uaccess.h> -+ -+static struct dentry *dfs_root; /* debugfs root directory */ -+ -+/******************************************************************************* -+ * Query Buffer Pool State -+ ******************************************************************************/ -+static int query_bp_state_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct bm_pool_state state; -+ int i, j; -+ u32 mask; -+ -+ memset(&state, 0, sizeof(struct bm_pool_state)); -+ ret = bman_query_pools(&state); -+ if (ret) { -+ seq_printf(file, "Error %d\n", ret); -+ return 0; -+ } -+ seq_puts(file, "bp_id free_buffers_avail bp_depleted\n"); -+ for (i = 0; i < 2; i++) { -+ mask = 0x80000000; -+ for (j = 0; j < 32; j++) { -+ seq_printf(file, -+ " %-2u %-3s %-3s\n", -+ (i*32)+j, -+ (state.as.state.__state[i] & mask) ? "no" : "yes", -+ (state.ds.state.__state[i] & mask) ? "yes" : "no"); -+ mask >>= 1; -+ } -+ } -+ return 0; -+} -+ -+static int query_bp_state_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, query_bp_state_show, NULL); -+} -+ -+static const struct file_operations query_bp_state_fops = { -+ .owner = THIS_MODULE, -+ .open = query_bp_state_open, -+ .read = seq_read, -+ .release = single_release, -+}; -+ -+static int __init bman_debugfs_module_init(void) -+{ -+ int ret = 0; -+ struct dentry *d; -+ -+ dfs_root = debugfs_create_dir("bman", NULL); -+ -+ if (dfs_root == NULL) { -+ ret = -ENOMEM; -+ pr_err("Cannot create bman debugfs dir\n"); -+ goto _return; -+ } -+ d = debugfs_create_file("query_bp_state", -+ S_IRUGO, -+ dfs_root, -+ NULL, -+ &query_bp_state_fops); -+ if (d == NULL) { -+ ret = -ENOMEM; -+ pr_err("Cannot create query_bp_state\n"); -+ goto _return; -+ } -+ return 0; -+ -+_return: -+ debugfs_remove_recursive(dfs_root); -+ return ret; -+} -+ -+static void __exit bman_debugfs_module_exit(void) -+{ -+ debugfs_remove_recursive(dfs_root); -+} -+ -+ -+module_init(bman_debugfs_module_init); -+module_exit(bman_debugfs_module_exit); -+MODULE_LICENSE("Dual BSD/GPL"); ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_driver.c -@@ -0,0 +1,559 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+#include "bman_low.h" -+#ifdef CONFIG_HOTPLUG_CPU -+#include <linux/cpu.h> -+#endif -+/* -+ * Global variables of the max portal/pool number this bman version supported -+ */ -+u16 bman_ip_rev; -+EXPORT_SYMBOL(bman_ip_rev); -+u16 bman_pool_max; -+EXPORT_SYMBOL(bman_pool_max); -+static u16 bman_portal_max; -+ -+/* After initialising cpus that own shared portal configs, we cache the -+ * resulting portals (ie. not just the configs) in this array. Then we -+ * initialise slave cpus that don't have their own portals, redirecting them to -+ * portals from this cache in a round-robin assignment. */ -+static struct bman_portal *shared_portals[NR_CPUS]; -+static int num_shared_portals; -+static int shared_portals_idx; -+static LIST_HEAD(unused_pcfgs); -+static DEFINE_SPINLOCK(unused_pcfgs_lock); -+static void *affine_bportals[NR_CPUS]; -+ -+static int __init fsl_bpool_init(struct device_node *node) -+{ -+ int ret; -+ u32 *thresh, *bpid = (u32 *)of_get_property(node, "fsl,bpid", &ret); -+ if (!bpid || (ret != 4)) { -+ pr_err("Can't get %s property 'fsl,bpid'\n", node->full_name); -+ return -ENODEV; -+ } -+ thresh = (u32 *)of_get_property(node, "fsl,bpool-thresholds", &ret); -+ if (thresh) { -+ if (ret != 16) { -+ pr_err("Invalid %s property '%s'\n", -+ node->full_name, "fsl,bpool-thresholds"); -+ return -ENODEV; -+ } -+ } -+ if (thresh) { -+#ifdef CONFIG_FSL_BMAN_CONFIG -+ ret = bm_pool_set(be32_to_cpu(*bpid), thresh); -+ if (ret) -+ pr_err("No CCSR node for %s property '%s'\n", -+ node->full_name, "fsl,bpool-thresholds"); -+ return ret; -+#else -+ pr_err("Ignoring %s property '%s', no CCSR support\n", -+ node->full_name, "fsl,bpool-thresholds"); -+#endif -+ } -+ return 0; -+} -+ -+static int __init fsl_bpid_range_init(struct device_node *node) -+{ -+ int ret; -+ u32 *range = (u32 *)of_get_property(node, "fsl,bpid-range", &ret); -+ if (!range) { -+ pr_err("No 'fsl,bpid-range' property in node %s\n", -+ node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err("'fsl,bpid-range' is not a 2-cell range in node %s\n", -+ node->full_name); -+ return -EINVAL; -+ } -+ bman_seed_bpid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ pr_info("Bman: BPID allocator includes range %d:%d\n", -+ be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ return 0; -+} -+ -+static struct bm_portal_config * __init parse_pcfg(struct device_node *node) -+{ -+ struct bm_portal_config *pcfg; -+ const u32 *index; -+ int irq, ret; -+ resource_size_t len; -+ -+ pcfg = kmalloc(sizeof(*pcfg), GFP_KERNEL); -+ if (!pcfg) { -+ pr_err("can't allocate portal config"); -+ return NULL; -+ } -+ -+ if (of_device_is_compatible(node, "fsl,bman-portal-1.0") || -+ of_device_is_compatible(node, "fsl,bman-portal-1.0.0")) { -+ bman_ip_rev = BMAN_REV10; -+ bman_pool_max = 64; -+ bman_portal_max = 10; -+ } else if (of_device_is_compatible(node, "fsl,bman-portal-2.0") || -+ of_device_is_compatible(node, "fsl,bman-portal-2.0.8")) { -+ bman_ip_rev = BMAN_REV20; -+ bman_pool_max = 8; -+ bman_portal_max = 3; -+ } else if (of_device_is_compatible(node, "fsl,bman-portal-2.1.0")) { -+ bman_ip_rev = BMAN_REV21; -+ bman_pool_max = 64; -+ bman_portal_max = 50; -+ } else if (of_device_is_compatible(node, "fsl,bman-portal-2.1.1")) { -+ bman_ip_rev = BMAN_REV21; -+ bman_pool_max = 64; -+ bman_portal_max = 25; -+ } else if (of_device_is_compatible(node, "fsl,bman-portal-2.1.2")) { -+ bman_ip_rev = BMAN_REV21; -+ bman_pool_max = 64; -+ bman_portal_max = 18; -+ } else if (of_device_is_compatible(node, "fsl,bman-portal-2.1.3")) { -+ bman_ip_rev = BMAN_REV21; -+ bman_pool_max = 64; -+ bman_portal_max = 10; -+ } else { -+ pr_warn("unknown BMan version in portal node," -+ "default to rev1.0\n"); -+ bman_ip_rev = BMAN_REV10; -+ bman_pool_max = 64; -+ bman_portal_max = 10; -+ } -+ -+ ret = of_address_to_resource(node, DPA_PORTAL_CE, -+ &pcfg->addr_phys[DPA_PORTAL_CE]); -+ if (ret) { -+ pr_err("Can't get %s property 'reg::CE'\n", node->full_name); -+ goto err; -+ } -+ ret = of_address_to_resource(node, DPA_PORTAL_CI, -+ &pcfg->addr_phys[DPA_PORTAL_CI]); -+ if (ret) { -+ pr_err("Can't get %s property 'reg::CI'\n", node->full_name); -+ goto err; -+ } -+ -+ index = of_get_property(node, "cell-index", &ret); -+ if (!index || (ret != 4)) { -+ pr_err("Can't get %s property '%s'\n", node->full_name, -+ "cell-index"); -+ goto err; -+ } -+ if (be32_to_cpu(*index) >= bman_portal_max) { -+ pr_err("BMan portal cell index %d out of range, max %d\n", -+ be32_to_cpu(*index), bman_portal_max); -+ goto err; -+ } -+ -+ pcfg->public_cfg.cpu = -1; -+ -+ irq = irq_of_parse_and_map(node, 0); -+ if (irq == 0) { -+ pr_err("Can't get %s property 'interrupts'\n", node->full_name); -+ goto err; -+ } -+ pcfg->public_cfg.irq = irq; -+ pcfg->public_cfg.index = be32_to_cpu(*index); -+ bman_depletion_fill(&pcfg->public_cfg.mask); -+ -+ len = resource_size(&pcfg->addr_phys[DPA_PORTAL_CE]); -+ if (len != (unsigned long)len) -+ goto err; -+ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_cache_ns( -+ pcfg->addr_phys[DPA_PORTAL_CE].start, -+ resource_size(&pcfg->addr_phys[DPA_PORTAL_CE])); -+ pcfg->addr_virt[DPA_PORTAL_CI] = ioremap( -+ pcfg->addr_phys[DPA_PORTAL_CI].start, -+ resource_size(&pcfg->addr_phys[DPA_PORTAL_CI])); -+ -+#else -+ pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_prot( -+ pcfg->addr_phys[DPA_PORTAL_CE].start, -+ (unsigned long)len, -+ 0); -+ pcfg->addr_virt[DPA_PORTAL_CI] = ioremap_prot( -+ pcfg->addr_phys[DPA_PORTAL_CI].start, -+ resource_size(&pcfg->addr_phys[DPA_PORTAL_CI]), -+ _PAGE_GUARDED | _PAGE_NO_CACHE); -+#endif -+ /* disable bp depletion */ -+ __raw_writel(0x0, pcfg->addr_virt[DPA_PORTAL_CI] + BM_REG_SCN(0)); -+ __raw_writel(0x0, pcfg->addr_virt[DPA_PORTAL_CI] + BM_REG_SCN(1)); -+ return pcfg; -+err: -+ kfree(pcfg); -+ return NULL; -+} -+ -+static struct bm_portal_config *get_pcfg(struct list_head *list) -+{ -+ struct bm_portal_config *pcfg; -+ if (list_empty(list)) -+ return NULL; -+ pcfg = list_entry(list->prev, struct bm_portal_config, list); -+ list_del(&pcfg->list); -+ return pcfg; -+} -+ -+static struct bm_portal_config *get_pcfg_idx(struct list_head *list, -+ uint32_t idx) -+{ -+ struct bm_portal_config *pcfg; -+ if (list_empty(list)) -+ return NULL; -+ list_for_each_entry(pcfg, list, list) { -+ if (pcfg->public_cfg.index == idx) { -+ list_del(&pcfg->list); -+ return pcfg; -+ } -+ } -+ return NULL; -+} -+ -+struct bm_portal_config *bm_get_unused_portal(void) -+{ -+ return bm_get_unused_portal_idx(QBMAN_ANY_PORTAL_IDX); -+} -+ -+struct bm_portal_config *bm_get_unused_portal_idx(uint32_t idx) -+{ -+ struct bm_portal_config *ret; -+ spin_lock(&unused_pcfgs_lock); -+ if (idx == QBMAN_ANY_PORTAL_IDX) -+ ret = get_pcfg(&unused_pcfgs); -+ else -+ ret = get_pcfg_idx(&unused_pcfgs, idx); -+ spin_unlock(&unused_pcfgs_lock); -+ return ret; -+} -+ -+void bm_put_unused_portal(struct bm_portal_config *pcfg) -+{ -+ spin_lock(&unused_pcfgs_lock); -+ list_add(&pcfg->list, &unused_pcfgs); -+ spin_unlock(&unused_pcfgs_lock); -+} -+ -+static struct bman_portal *init_pcfg(struct bm_portal_config *pcfg) -+{ -+ struct bman_portal *p; -+ p = bman_create_affine_portal(pcfg); -+ if (p) { -+#ifdef CONFIG_FSL_DPA_PIRQ_SLOW -+ bman_p_irqsource_add(p, BM_PIRQ_RCRI | BM_PIRQ_BSCN); -+#endif -+ pr_info("Bman portal %sinitialised, cpu %d\n", -+ pcfg->public_cfg.is_shared ? "(shared) " : "", -+ pcfg->public_cfg.cpu); -+ affine_bportals[pcfg->public_cfg.cpu] = p; -+ } else -+ pr_crit("Bman portal failure on cpu %d\n", -+ pcfg->public_cfg.cpu); -+ return p; -+} -+ -+static void init_slave(int cpu) -+{ -+ struct bman_portal *p; -+ p = bman_create_affine_slave(shared_portals[shared_portals_idx++], cpu); -+ if (!p) -+ pr_err("Bman slave portal failure on cpu %d\n", cpu); -+ else -+ pr_info("Bman portal %sinitialised, cpu %d\n", "(slave) ", cpu); -+ if (shared_portals_idx >= num_shared_portals) -+ shared_portals_idx = 0; -+ affine_bportals[cpu] = p; -+} -+ -+/* Bootarg "bportals=[...]" has the same syntax as "qportals=", and so the -+ * parsing is in dpa_sys.h. The syntax is a comma-separated list of indexes -+ * and/or ranges of indexes, with each being optionally prefixed by "s" to -+ * explicitly mark it or them for sharing. -+ * Eg; -+ * bportals=s0,1-3,s4 -+ * means that cpus 1,2,3 get "unshared" portals, cpus 0 and 4 get "shared" -+ * portals, and any remaining cpus share the portals that are assigned to cpus 0 -+ * or 4, selected in a round-robin fashion. (In this example, cpu 5 would share -+ * cpu 0's portal, cpu 6 would share cpu4's portal, and cpu 7 would share cpu -+ * 0's portal.) */ -+static struct cpumask want_unshared __initdata; /* cpus requested without "s" */ -+static struct cpumask want_shared __initdata; /* cpus requested with "s" */ -+ -+static int __init parse_bportals(char *str) -+{ -+ return parse_portals_bootarg(str, &want_shared, &want_unshared, -+ "bportals"); -+} -+__setup("bportals=", parse_bportals); -+ -+static int bman_offline_cpu(unsigned int cpu) -+{ -+ struct bman_portal *p; -+ const struct bm_portal_config *pcfg; -+ p = (struct bman_portal *)affine_bportals[cpu]; -+ if (p) { -+ pcfg = bman_get_bm_portal_config(p); -+ if (pcfg) -+ irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(0)); -+ } -+ return 0; -+} -+ -+#ifdef CONFIG_HOTPLUG_CPU -+static int bman_online_cpu(unsigned int cpu) -+{ -+ struct bman_portal *p; -+ const struct bm_portal_config *pcfg; -+ p = (struct bman_portal *)affine_bportals[cpu]; -+ if (p) { -+ pcfg = bman_get_bm_portal_config(p); -+ if (pcfg) -+ irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(cpu)); -+ } -+ return 0; -+} -+#endif /* CONFIG_HOTPLUG_CPU */ -+ -+/* Initialise the Bman driver. The meat of this function deals with portals. The -+ * following describes the flow of portal-handling, the code "steps" refer to -+ * this description; -+ * 1. Portal configs are parsed from the device-tree into 'unused_pcfgs', with -+ * ::cpu==-1. Regions and interrupts are mapped (but interrupts are not -+ * bound). -+ * 2. The "want_shared" and "want_unshared" lists (as filled by the -+ * "bportals=[...]" bootarg) are processed, allocating portals and assigning -+ * them to cpus, placing them in the relevant list and setting ::cpu as -+ * appropriate. If no "bportals" bootarg was present, the defaut is to try to -+ * assign portals to all online cpus at the time of driver initialisation. -+ * Any failure to allocate portals (when parsing the "want" lists or when -+ * using default behaviour) will be silently tolerated (the "fixup" logic in -+ * step 3 will determine what happens in this case). -+ * 3. Do fixups relative to cpu_online_mask(). If no portals are marked for -+ * sharing and sharing is required (because not all cpus have been assigned -+ * portals), then one portal will marked for sharing. Conversely if no -+ * sharing is required, any portals marked for sharing will not be shared. It -+ * may be that sharing occurs when it wasn't expected, if portal allocation -+ * failed to honour all the requested assignments (including the default -+ * assignments if no bootarg is present). -+ * 4. Unshared portals are initialised on their respective cpus. -+ * 5. Shared portals are initialised on their respective cpus. -+ * 6. Each remaining cpu is initialised to slave to one of the shared portals, -+ * which are selected in a round-robin fashion. -+ * Any portal configs left unused are available for USDPAA allocation. -+ */ -+__init int bman_init(void) -+{ -+ struct cpumask slave_cpus; -+ struct cpumask unshared_cpus = *cpu_none_mask; -+ struct cpumask shared_cpus = *cpu_none_mask; -+ LIST_HEAD(unshared_pcfgs); -+ LIST_HEAD(shared_pcfgs); -+ struct device_node *dn; -+ struct bm_portal_config *pcfg; -+ struct bman_portal *p; -+ int cpu, ret; -+ struct cpumask offline_cpus; -+ -+ /* Initialise the Bman (CCSR) device */ -+ for_each_compatible_node(dn, NULL, "fsl,bman") { -+ if (!bman_init_ccsr(dn)) -+ pr_info("Bman err interrupt handler present\n"); -+ else -+ pr_err("Bman CCSR setup failed\n"); -+ } -+ /* Initialise any declared buffer pools */ -+ for_each_compatible_node(dn, NULL, "fsl,bpool") { -+ ret = fsl_bpool_init(dn); -+ if (ret) -+ return ret; -+ } -+ /* Step 1. See comments at the beginning of the file. */ -+ for_each_compatible_node(dn, NULL, "fsl,bman-portal") { -+ if (!of_device_is_available(dn)) -+ continue; -+ pcfg = parse_pcfg(dn); -+ if (pcfg) -+ list_add_tail(&pcfg->list, &unused_pcfgs); -+ } -+ /* Step 2. */ -+ for_each_possible_cpu(cpu) { -+ if (cpumask_test_cpu(cpu, &want_shared)) { -+ pcfg = get_pcfg(&unused_pcfgs); -+ if (!pcfg) -+ break; -+ pcfg->public_cfg.cpu = cpu; -+ list_add_tail(&pcfg->list, &shared_pcfgs); -+ cpumask_set_cpu(cpu, &shared_cpus); -+ } -+ if (cpumask_test_cpu(cpu, &want_unshared)) { -+ if (cpumask_test_cpu(cpu, &shared_cpus)) -+ continue; -+ pcfg = get_pcfg(&unused_pcfgs); -+ if (!pcfg) -+ break; -+ pcfg->public_cfg.cpu = cpu; -+ list_add_tail(&pcfg->list, &unshared_pcfgs); -+ cpumask_set_cpu(cpu, &unshared_cpus); -+ } -+ } -+ if (list_empty(&shared_pcfgs) && list_empty(&unshared_pcfgs)) { -+ /* Default, give an unshared portal to each online cpu */ -+ for_each_online_cpu(cpu) { -+ pcfg = get_pcfg(&unused_pcfgs); -+ if (!pcfg) -+ break; -+ pcfg->public_cfg.cpu = cpu; -+ list_add_tail(&pcfg->list, &unshared_pcfgs); -+ cpumask_set_cpu(cpu, &unshared_cpus); -+ } -+ } -+ /* Step 3. */ -+ cpumask_andnot(&slave_cpus, cpu_possible_mask, &shared_cpus); -+ cpumask_andnot(&slave_cpus, &slave_cpus, &unshared_cpus); -+ if (cpumask_empty(&slave_cpus)) { -+ /* No sharing required */ -+ if (!list_empty(&shared_pcfgs)) { -+ /* Migrate "shared" to "unshared" */ -+ cpumask_or(&unshared_cpus, &unshared_cpus, -+ &shared_cpus); -+ cpumask_clear(&shared_cpus); -+ list_splice_tail(&shared_pcfgs, &unshared_pcfgs); -+ INIT_LIST_HEAD(&shared_pcfgs); -+ } -+ } else { -+ /* Sharing required */ -+ if (list_empty(&shared_pcfgs)) { -+ /* Migrate one "unshared" to "shared" */ -+ pcfg = get_pcfg(&unshared_pcfgs); -+ if (!pcfg) { -+ pr_crit("No BMan portals available!\n"); -+ return 0; -+ } -+ cpumask_clear_cpu(pcfg->public_cfg.cpu, &unshared_cpus); -+ cpumask_set_cpu(pcfg->public_cfg.cpu, &shared_cpus); -+ list_add_tail(&pcfg->list, &shared_pcfgs); -+ } -+ } -+ /* Step 4. */ -+ list_for_each_entry(pcfg, &unshared_pcfgs, list) { -+ pcfg->public_cfg.is_shared = 0; -+ p = init_pcfg(pcfg); -+ if (!p) { -+ pr_crit("Unable to initialize bman portal\n"); -+ return 0; -+ } -+ } -+ /* Step 5. */ -+ list_for_each_entry(pcfg, &shared_pcfgs, list) { -+ pcfg->public_cfg.is_shared = 1; -+ p = init_pcfg(pcfg); -+ if (p) -+ shared_portals[num_shared_portals++] = p; -+ } -+ /* Step 6. */ -+ if (!cpumask_empty(&slave_cpus)) -+ for_each_cpu(cpu, &slave_cpus) -+ init_slave(cpu); -+ pr_info("Bman portals initialised\n"); -+ cpumask_andnot(&offline_cpus, cpu_possible_mask, cpu_online_mask); -+ for_each_cpu(cpu, &offline_cpus) -+ bman_offline_cpu(cpu); -+#ifdef CONFIG_HOTPLUG_CPU -+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, -+ "soc/qbman_portal:online", -+ bman_online_cpu, bman_offline_cpu); -+ if (ret < 0) { -+ pr_err("bman: failed to register hotplug callbacks.\n"); -+ return 0; -+ } -+#endif -+ return 0; -+} -+ -+__init int bman_resource_init(void) -+{ -+ struct device_node *dn; -+ int ret; -+ -+ /* Initialise BPID allocation ranges */ -+ for_each_compatible_node(dn, NULL, "fsl,bpid-range") { -+ ret = fsl_bpid_range_init(dn); -+ if (ret) -+ return ret; -+ } -+ return 0; -+} -+ -+#ifdef CONFIG_SUSPEND -+void suspend_unused_bportal(void) -+{ -+ struct bm_portal_config *pcfg; -+ -+ if (list_empty(&unused_pcfgs)) -+ return; -+ -+ list_for_each_entry(pcfg, &unused_pcfgs, list) { -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Need to save bportal %d\n", pcfg->public_cfg.index); -+#endif -+ /* save isdr, disable all via isdr, clear isr */ -+ pcfg->saved_isdr = -+ __raw_readl(pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08); -+ __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] + -+ 0xe08); -+ __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] + -+ 0xe00); -+ } -+ return; -+} -+ -+void resume_unused_bportal(void) -+{ -+ struct bm_portal_config *pcfg; -+ -+ if (list_empty(&unused_pcfgs)) -+ return; -+ -+ list_for_each_entry(pcfg, &unused_pcfgs, list) { -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Need to resume bportal %d\n", pcfg->public_cfg.index); -+#endif -+ /* restore isdr */ -+ __raw_writel(pcfg->saved_isdr, -+ pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08); -+ } -+ return; -+} -+#endif ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_high.c -@@ -0,0 +1,1145 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "bman_low.h" -+ -+/* Compilation constants */ -+#define RCR_THRESH 2 /* reread h/w CI when running out of space */ -+#define IRQNAME "BMan portal %d" -+#define MAX_IRQNAME 16 /* big enough for "BMan portal %d" */ -+ -+struct bman_portal { -+ struct bm_portal p; -+ /* 2-element array. pools[0] is mask, pools[1] is snapshot. */ -+ struct bman_depletion *pools; -+ int thresh_set; -+ unsigned long irq_sources; -+ u32 slowpoll; /* only used when interrupts are off */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ struct bman_pool *rcri_owned; /* only 1 release WAIT_SYNC at a time */ -+#endif -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ raw_spinlock_t sharing_lock; /* only used if is_shared */ -+ int is_shared; -+ struct bman_portal *sharing_redirect; -+#endif -+ /* When the cpu-affine portal is activated, this is non-NULL */ -+ const struct bm_portal_config *config; -+ /* This is needed for power management */ -+ struct platform_device *pdev; -+ /* 64-entry hash-table of pool objects that are tracking depletion -+ * entry/exit (ie. BMAN_POOL_FLAG_DEPLETION). This isn't fast-path, so -+ * we're not fussy about cache-misses and so forth - whereas the above -+ * members should all fit in one cacheline. -+ * BTW, with 64 entries in the hash table and 64 buffer pools to track, -+ * you'll never guess the hash-function ... */ -+ struct bman_pool *cb[64]; -+ char irqname[MAX_IRQNAME]; -+ /* Track if the portal was alloced by the driver */ -+ u8 alloced; -+ /* power management data */ -+ u32 save_isdr; -+}; -+ -+/* For an explanation of the locking, redirection, or affine-portal logic, -+ * please consult the Qman driver for details. This is the same, only simpler -+ * (no fiddly Qman-specific bits.) */ -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+#define PORTAL_IRQ_LOCK(p, irqflags) \ -+ do { \ -+ if ((p)->is_shared) \ -+ raw_spin_lock_irqsave(&(p)->sharing_lock, irqflags); \ -+ else \ -+ local_irq_save(irqflags); \ -+ } while (0) -+#define PORTAL_IRQ_UNLOCK(p, irqflags) \ -+ do { \ -+ if ((p)->is_shared) \ -+ raw_spin_unlock_irqrestore(&(p)->sharing_lock, \ -+ irqflags); \ -+ else \ -+ local_irq_restore(irqflags); \ -+ } while (0) -+#else -+#define PORTAL_IRQ_LOCK(p, irqflags) local_irq_save(irqflags) -+#define PORTAL_IRQ_UNLOCK(p, irqflags) local_irq_restore(irqflags) -+#endif -+ -+static cpumask_t affine_mask; -+static DEFINE_SPINLOCK(affine_mask_lock); -+static DEFINE_PER_CPU(struct bman_portal, bman_affine_portal); -+static inline struct bman_portal *get_raw_affine_portal(void) -+{ -+ return &get_cpu_var(bman_affine_portal); -+} -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+static inline struct bman_portal *get_affine_portal(void) -+{ -+ struct bman_portal *p = get_raw_affine_portal(); -+ if (p->sharing_redirect) -+ return p->sharing_redirect; -+ return p; -+} -+#else -+#define get_affine_portal() get_raw_affine_portal() -+#endif -+static inline void put_affine_portal(void) -+{ -+ put_cpu_var(bman_affine_portal); -+} -+static inline struct bman_portal *get_poll_portal(void) -+{ -+ return &get_cpu_var(bman_affine_portal); -+} -+#define put_poll_portal() -+ -+/* GOTCHA: this object type refers to a pool, it isn't *the* pool. There may be -+ * more than one such object per Bman buffer pool, eg. if different users of the -+ * pool are operating via different portals. */ -+struct bman_pool { -+ struct bman_pool_params params; -+ /* Used for hash-table admin when using depletion notifications. */ -+ struct bman_portal *portal; -+ struct bman_pool *next; -+ /* stockpile state - NULL unless BMAN_POOL_FLAG_STOCKPILE is set */ -+ struct bm_buffer *sp; -+ unsigned int sp_fill; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ atomic_t in_use; -+#endif -+}; -+ -+/* (De)Registration of depletion notification callbacks */ -+static void depletion_link(struct bman_portal *portal, struct bman_pool *pool) -+{ -+ __maybe_unused unsigned long irqflags; -+ pool->portal = portal; -+ PORTAL_IRQ_LOCK(portal, irqflags); -+ pool->next = portal->cb[pool->params.bpid]; -+ portal->cb[pool->params.bpid] = pool; -+ if (!pool->next) -+ /* First object for that bpid on this portal, enable the BSCN -+ * mask bit. */ -+ bm_isr_bscn_mask(&portal->p, pool->params.bpid, 1); -+ PORTAL_IRQ_UNLOCK(portal, irqflags); -+} -+static void depletion_unlink(struct bman_pool *pool) -+{ -+ struct bman_pool *it, *last = NULL; -+ struct bman_pool **base = &pool->portal->cb[pool->params.bpid]; -+ __maybe_unused unsigned long irqflags; -+ PORTAL_IRQ_LOCK(pool->portal, irqflags); -+ it = *base; /* <-- gotcha, don't do this prior to the irq_save */ -+ while (it != pool) { -+ last = it; -+ it = it->next; -+ } -+ if (!last) -+ *base = pool->next; -+ else -+ last->next = pool->next; -+ if (!last && !pool->next) { -+ /* Last object for that bpid on this portal, disable the BSCN -+ * mask bit. */ -+ bm_isr_bscn_mask(&pool->portal->p, pool->params.bpid, 0); -+ /* And "forget" that we last saw this pool as depleted */ -+ bman_depletion_unset(&pool->portal->pools[1], -+ pool->params.bpid); -+ } -+ PORTAL_IRQ_UNLOCK(pool->portal, irqflags); -+} -+ -+/* In the case that the application's core loop calls qman_poll() and -+ * bman_poll(), we ought to balance how often we incur the overheads of the -+ * slow-path poll. We'll use two decrementer sources. The idle decrementer -+ * constant is used when the last slow-poll detected no work to do, and the busy -+ * decrementer constant when the last slow-poll had work to do. */ -+#define SLOW_POLL_IDLE 1000 -+#define SLOW_POLL_BUSY 10 -+static u32 __poll_portal_slow(struct bman_portal *p, u32 is); -+ -+/* Portal interrupt handler */ -+static irqreturn_t portal_isr(__always_unused int irq, void *ptr) -+{ -+ struct bman_portal *p = ptr; -+ u32 clear = p->irq_sources; -+ u32 is = bm_isr_status_read(&p->p) & p->irq_sources; -+ clear |= __poll_portal_slow(p, is); -+ bm_isr_status_clear(&p->p, clear); -+ return IRQ_HANDLED; -+} -+ -+#ifdef CONFIG_SUSPEND -+static int _bman_portal_suspend_noirq(struct device *dev) -+{ -+ struct bman_portal *p = (struct bman_portal *)dev->platform_data; -+#ifdef CONFIG_PM_DEBUG -+ struct platform_device *pdev = to_platform_device(dev); -+#endif -+ p->save_isdr = bm_isr_disable_read(&p->p); -+ bm_isr_disable_write(&p->p, 0xffffffff); -+ bm_isr_status_clear(&p->p, 0xffffffff); -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Suspend for %s\n", pdev->name); -+#endif -+ return 0; -+} -+ -+static int _bman_portal_resume_noirq(struct device *dev) -+{ -+ struct bman_portal *p = (struct bman_portal *)dev->platform_data; -+ -+ /* restore isdr */ -+ bm_isr_disable_write(&p->p, p->save_isdr); -+ return 0; -+} -+#else -+#define _bman_portal_suspend_noirq NULL -+#define _bman_portal_resume_noirq NULL -+#endif -+ -+struct dev_pm_domain bman_portal_device_pm_domain = { -+ .ops = { -+ USE_PLATFORM_PM_SLEEP_OPS -+ .suspend_noirq = _bman_portal_suspend_noirq, -+ .resume_noirq = _bman_portal_resume_noirq, -+ } -+}; -+ -+struct bman_portal *bman_create_portal( -+ struct bman_portal *portal, -+ const struct bm_portal_config *config) -+{ -+ struct bm_portal *__p; -+ const struct bman_depletion *pools = &config->public_cfg.mask; -+ int ret; -+ u8 bpid = 0; -+ char buf[16]; -+ -+ if (!portal) { -+ portal = kmalloc(sizeof(*portal), GFP_KERNEL); -+ if (!portal) -+ return portal; -+ portal->alloced = 1; -+ } else -+ portal->alloced = 0; -+ -+ __p = &portal->p; -+ -+ /* prep the low-level portal struct with the mapped addresses from the -+ * config, everything that follows depends on it and "config" is more -+ * for (de)reference... */ -+ __p->addr.addr_ce = config->addr_virt[DPA_PORTAL_CE]; -+ __p->addr.addr_ci = config->addr_virt[DPA_PORTAL_CI]; -+ if (bm_rcr_init(__p, bm_rcr_pvb, bm_rcr_cce)) { -+ pr_err("Bman RCR initialisation failed\n"); -+ goto fail_rcr; -+ } -+ if (bm_mc_init(__p)) { -+ pr_err("Bman MC initialisation failed\n"); -+ goto fail_mc; -+ } -+ if (bm_isr_init(__p)) { -+ pr_err("Bman ISR initialisation failed\n"); -+ goto fail_isr; -+ } -+ portal->pools = kmalloc(2 * sizeof(*pools), GFP_KERNEL); -+ if (!portal->pools) -+ goto fail_pools; -+ portal->pools[0] = *pools; -+ bman_depletion_init(portal->pools + 1); -+ while (bpid < bman_pool_max) { -+ /* Default to all BPIDs disabled, we enable as required at -+ * run-time. */ -+ bm_isr_bscn_mask(__p, bpid, 0); -+ bpid++; -+ } -+ portal->slowpoll = 0; -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ portal->rcri_owned = NULL; -+#endif -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ raw_spin_lock_init(&portal->sharing_lock); -+ portal->is_shared = config->public_cfg.is_shared; -+ portal->sharing_redirect = NULL; -+#endif -+ sprintf(buf, "bportal-%u", config->public_cfg.index); -+ portal->pdev = platform_device_alloc(buf, -1); -+ if (!portal->pdev) -+ goto fail_devalloc; -+ portal->pdev->dev.pm_domain = &bman_portal_device_pm_domain; -+ portal->pdev->dev.platform_data = portal; -+ ret = platform_device_add(portal->pdev); -+ if (ret) -+ goto fail_devadd; -+ memset(&portal->cb, 0, sizeof(portal->cb)); -+ /* Write-to-clear any stale interrupt status bits */ -+ bm_isr_disable_write(__p, 0xffffffff); -+ portal->irq_sources = 0; -+ bm_isr_enable_write(__p, portal->irq_sources); -+ bm_isr_status_clear(__p, 0xffffffff); -+ snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, config->public_cfg.cpu); -+ if (request_irq(config->public_cfg.irq, portal_isr, 0, portal->irqname, -+ portal)) { -+ pr_err("request_irq() failed\n"); -+ goto fail_irq; -+ } -+ if ((config->public_cfg.cpu != -1) && -+ irq_can_set_affinity(config->public_cfg.irq) && -+ irq_set_affinity(config->public_cfg.irq, -+ cpumask_of(config->public_cfg.cpu))) { -+ pr_err("irq_set_affinity() failed %s\n", portal->irqname); -+ goto fail_affinity; -+ } -+ -+ /* Need RCR to be empty before continuing */ -+ ret = bm_rcr_get_fill(__p); -+ if (ret) { -+ pr_err("Bman RCR unclean\n"); -+ goto fail_rcr_empty; -+ } -+ /* Success */ -+ portal->config = config; -+ -+ bm_isr_disable_write(__p, 0); -+ bm_isr_uninhibit(__p); -+ return portal; -+fail_rcr_empty: -+fail_affinity: -+ free_irq(config->public_cfg.irq, portal); -+fail_irq: -+ platform_device_del(portal->pdev); -+fail_devadd: -+ platform_device_put(portal->pdev); -+fail_devalloc: -+ kfree(portal->pools); -+fail_pools: -+ bm_isr_finish(__p); -+fail_isr: -+ bm_mc_finish(__p); -+fail_mc: -+ bm_rcr_finish(__p); -+fail_rcr: -+ if (portal->alloced) -+ kfree(portal); -+ return NULL; -+} -+ -+struct bman_portal *bman_create_affine_portal( -+ const struct bm_portal_config *config) -+{ -+ struct bman_portal *portal; -+ -+ portal = &per_cpu(bman_affine_portal, config->public_cfg.cpu); -+ portal = bman_create_portal(portal, config); -+ if (portal) { -+ spin_lock(&affine_mask_lock); -+ cpumask_set_cpu(config->public_cfg.cpu, &affine_mask); -+ spin_unlock(&affine_mask_lock); -+ } -+ return portal; -+} -+ -+ -+struct bman_portal *bman_create_affine_slave(struct bman_portal *redirect, -+ int cpu) -+{ -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ struct bman_portal *p; -+ p = &per_cpu(bman_affine_portal, cpu); -+ BUG_ON(p->config); -+ BUG_ON(p->is_shared); -+ BUG_ON(!redirect->config->public_cfg.is_shared); -+ p->irq_sources = 0; -+ p->sharing_redirect = redirect; -+ return p; -+#else -+ BUG(); -+ return NULL; -+#endif -+} -+ -+void bman_destroy_portal(struct bman_portal *bm) -+{ -+ const struct bm_portal_config *pcfg; -+ pcfg = bm->config; -+ bm_rcr_cce_update(&bm->p); -+ bm_rcr_cce_update(&bm->p); -+ -+ free_irq(pcfg->public_cfg.irq, bm); -+ -+ kfree(bm->pools); -+ bm_isr_finish(&bm->p); -+ bm_mc_finish(&bm->p); -+ bm_rcr_finish(&bm->p); -+ bm->config = NULL; -+ if (bm->alloced) -+ kfree(bm); -+} -+ -+const struct bm_portal_config *bman_destroy_affine_portal(void) -+{ -+ struct bman_portal *bm = get_raw_affine_portal(); -+ const struct bm_portal_config *pcfg; -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (bm->sharing_redirect) { -+ bm->sharing_redirect = NULL; -+ put_affine_portal(); -+ return NULL; -+ } -+ bm->is_shared = 0; -+#endif -+ pcfg = bm->config; -+ bman_destroy_portal(bm); -+ spin_lock(&affine_mask_lock); -+ cpumask_clear_cpu(pcfg->public_cfg.cpu, &affine_mask); -+ spin_unlock(&affine_mask_lock); -+ put_affine_portal(); -+ return pcfg; -+} -+ -+/* When release logic waits on available RCR space, we need a global waitqueue -+ * in the case of "affine" use (as the waits wake on different cpus which means -+ * different portals - so we can't wait on any per-portal waitqueue). */ -+static DECLARE_WAIT_QUEUE_HEAD(affine_queue); -+ -+static u32 __poll_portal_slow(struct bman_portal *p, u32 is) -+{ -+ struct bman_depletion tmp; -+ u32 ret = is; -+ -+ /* There is a gotcha to be aware of. If we do the query before clearing -+ * the status register, we may miss state changes that occur between the -+ * two. If we write to clear the status register before the query, the -+ * cache-enabled query command may overtake the status register write -+ * unless we use a heavyweight sync (which we don't want). Instead, we -+ * write-to-clear the status register then *read it back* before doing -+ * the query, hence the odd while loop with the 'is' accumulation. */ -+ if (is & BM_PIRQ_BSCN) { -+ struct bm_mc_result *mcr; -+ __maybe_unused unsigned long irqflags; -+ unsigned int i, j; -+ u32 __is; -+ bm_isr_status_clear(&p->p, BM_PIRQ_BSCN); -+ while ((__is = bm_isr_status_read(&p->p)) & BM_PIRQ_BSCN) { -+ is |= __is; -+ bm_isr_status_clear(&p->p, BM_PIRQ_BSCN); -+ } -+ is &= ~BM_PIRQ_BSCN; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ bm_mc_start(&p->p); -+ bm_mc_commit(&p->p, BM_MCC_VERB_CMD_QUERY); -+ while (!(mcr = bm_mc_result(&p->p))) -+ cpu_relax(); -+ tmp = mcr->query.ds.state; -+ tmp.__state[0] = be32_to_cpu(tmp.__state[0]); -+ tmp.__state[1] = be32_to_cpu(tmp.__state[1]); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ for (i = 0; i < 2; i++) { -+ int idx = i * 32; -+ /* tmp is a mask of currently-depleted pools. -+ * pools[0] is mask of those we care about. -+ * pools[1] is our previous view (we only want to -+ * be told about changes). */ -+ tmp.__state[i] &= p->pools[0].__state[i]; -+ if (tmp.__state[i] == p->pools[1].__state[i]) -+ /* fast-path, nothing to see, move along */ -+ continue; -+ for (j = 0; j <= 31; j++, idx++) { -+ struct bman_pool *pool = p->cb[idx]; -+ int b4 = bman_depletion_get(&p->pools[1], idx); -+ int af = bman_depletion_get(&tmp, idx); -+ if (b4 == af) -+ continue; -+ while (pool) { -+ pool->params.cb(p, pool, -+ pool->params.cb_ctx, af); -+ pool = pool->next; -+ } -+ } -+ } -+ p->pools[1] = tmp; -+ } -+ -+ if (is & BM_PIRQ_RCRI) { -+ __maybe_unused unsigned long irqflags; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ bm_rcr_cce_update(&p->p); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ /* If waiting for sync, we only cancel the interrupt threshold -+ * when the ring utilisation hits zero. */ -+ if (p->rcri_owned) { -+ if (!bm_rcr_get_fill(&p->p)) { -+ p->rcri_owned = NULL; -+ bm_rcr_set_ithresh(&p->p, 0); -+ } -+ } else -+#endif -+ bm_rcr_set_ithresh(&p->p, 0); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ wake_up(&affine_queue); -+ bm_isr_status_clear(&p->p, BM_PIRQ_RCRI); -+ is &= ~BM_PIRQ_RCRI; -+ } -+ -+ /* There should be no status register bits left undefined */ -+ DPA_ASSERT(!is); -+ return ret; -+} -+ -+const struct bman_portal_config *bman_get_portal_config(void) -+{ -+ struct bman_portal *p = get_affine_portal(); -+ const struct bman_portal_config *ret = &p->config->public_cfg; -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(bman_get_portal_config); -+ -+u32 bman_irqsource_get(void) -+{ -+ struct bman_portal *p = get_raw_affine_portal(); -+ u32 ret = p->irq_sources & BM_PIRQ_VISIBLE; -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(bman_irqsource_get); -+ -+int bman_p_irqsource_add(struct bman_portal *p, __maybe_unused u32 bits) -+{ -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (p->sharing_redirect) -+ return -EINVAL; -+ else -+#endif -+ { -+ __maybe_unused unsigned long irqflags; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ set_bits(bits & BM_PIRQ_VISIBLE, &p->irq_sources); -+ bm_isr_enable_write(&p->p, p->irq_sources); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ } -+ return 0; -+} -+EXPORT_SYMBOL(bman_p_irqsource_add); -+ -+int bman_irqsource_add(__maybe_unused u32 bits) -+{ -+ struct bman_portal *p = get_raw_affine_portal(); -+ int ret = 0; -+ ret = bman_p_irqsource_add(p, bits); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(bman_irqsource_add); -+ -+int bman_irqsource_remove(u32 bits) -+{ -+ struct bman_portal *p = get_raw_affine_portal(); -+ __maybe_unused unsigned long irqflags; -+ u32 ier; -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (p->sharing_redirect) { -+ put_affine_portal(); -+ return -EINVAL; -+ } -+#endif -+ /* Our interrupt handler only processes+clears status register bits that -+ * are in p->irq_sources. As we're trimming that mask, if one of them -+ * were to assert in the status register just before we remove it from -+ * the enable register, there would be an interrupt-storm when we -+ * release the IRQ lock. So we wait for the enable register update to -+ * take effect in h/w (by reading it back) and then clear all other bits -+ * in the status register. Ie. we clear them from ISR once it's certain -+ * IER won't allow them to reassert. */ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ bits &= BM_PIRQ_VISIBLE; -+ clear_bits(bits, &p->irq_sources); -+ bm_isr_enable_write(&p->p, p->irq_sources); -+ ier = bm_isr_enable_read(&p->p); -+ /* Using "~ier" (rather than "bits" or "~p->irq_sources") creates a -+ * data-dependency, ie. to protect against re-ordering. */ -+ bm_isr_status_clear(&p->p, ~ier); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return 0; -+} -+EXPORT_SYMBOL(bman_irqsource_remove); -+ -+const cpumask_t *bman_affine_cpus(void) -+{ -+ return &affine_mask; -+} -+EXPORT_SYMBOL(bman_affine_cpus); -+ -+u32 bman_poll_slow(void) -+{ -+ struct bman_portal *p = get_poll_portal(); -+ u32 ret; -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (unlikely(p->sharing_redirect)) -+ ret = (u32)-1; -+ else -+#endif -+ { -+ u32 is = bm_isr_status_read(&p->p) & ~p->irq_sources; -+ ret = __poll_portal_slow(p, is); -+ bm_isr_status_clear(&p->p, ret); -+ } -+ put_poll_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(bman_poll_slow); -+ -+/* Legacy wrapper */ -+void bman_poll(void) -+{ -+ struct bman_portal *p = get_poll_portal(); -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (unlikely(p->sharing_redirect)) -+ goto done; -+#endif -+ if (!(p->slowpoll--)) { -+ u32 is = bm_isr_status_read(&p->p) & ~p->irq_sources; -+ u32 active = __poll_portal_slow(p, is); -+ if (active) -+ p->slowpoll = SLOW_POLL_BUSY; -+ else -+ p->slowpoll = SLOW_POLL_IDLE; -+ } -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+done: -+#endif -+ put_poll_portal(); -+} -+EXPORT_SYMBOL(bman_poll); -+ -+static const u32 zero_thresholds[4] = {0, 0, 0, 0}; -+ -+struct bman_pool *bman_new_pool(const struct bman_pool_params *params) -+{ -+ struct bman_pool *pool = NULL; -+ u32 bpid; -+ -+ if (params->flags & BMAN_POOL_FLAG_DYNAMIC_BPID) { -+ int ret = bman_alloc_bpid(&bpid); -+ if (ret) -+ return NULL; -+ } else { -+ if (params->bpid >= bman_pool_max) -+ return NULL; -+ bpid = params->bpid; -+ } -+#ifdef CONFIG_FSL_BMAN_CONFIG -+ if (params->flags & BMAN_POOL_FLAG_THRESH) { -+ int ret = bm_pool_set(bpid, params->thresholds); -+ if (ret) -+ goto err; -+ } -+#else -+ if (params->flags & BMAN_POOL_FLAG_THRESH) -+ goto err; -+#endif -+ pool = kmalloc(sizeof(*pool), GFP_KERNEL); -+ if (!pool) -+ goto err; -+ pool->sp = NULL; -+ pool->sp_fill = 0; -+ pool->params = *params; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ atomic_set(&pool->in_use, 1); -+#endif -+ if (params->flags & BMAN_POOL_FLAG_DYNAMIC_BPID) -+ pool->params.bpid = bpid; -+ if (params->flags & BMAN_POOL_FLAG_STOCKPILE) { -+ pool->sp = kmalloc(sizeof(struct bm_buffer) * BMAN_STOCKPILE_SZ, -+ GFP_KERNEL); -+ if (!pool->sp) -+ goto err; -+ } -+ if (pool->params.flags & BMAN_POOL_FLAG_DEPLETION) { -+ struct bman_portal *p = get_affine_portal(); -+ if (!p->pools || !bman_depletion_get(&p->pools[0], bpid)) { -+ pr_err("Depletion events disabled for bpid %d\n", bpid); -+ goto err; -+ } -+ depletion_link(p, pool); -+ put_affine_portal(); -+ } -+ return pool; -+err: -+#ifdef CONFIG_FSL_BMAN_CONFIG -+ if (params->flags & BMAN_POOL_FLAG_THRESH) -+ bm_pool_set(bpid, zero_thresholds); -+#endif -+ if (params->flags & BMAN_POOL_FLAG_DYNAMIC_BPID) -+ bman_release_bpid(bpid); -+ if (pool) { -+ kfree(pool->sp); -+ kfree(pool); -+ } -+ return NULL; -+} -+EXPORT_SYMBOL(bman_new_pool); -+ -+void bman_free_pool(struct bman_pool *pool) -+{ -+#ifdef CONFIG_FSL_BMAN_CONFIG -+ if (pool->params.flags & BMAN_POOL_FLAG_THRESH) -+ bm_pool_set(pool->params.bpid, zero_thresholds); -+#endif -+ if (pool->params.flags & BMAN_POOL_FLAG_DEPLETION) -+ depletion_unlink(pool); -+ if (pool->params.flags & BMAN_POOL_FLAG_STOCKPILE) { -+ if (pool->sp_fill) -+ pr_err("Stockpile not flushed, has %u in bpid %u.\n", -+ pool->sp_fill, pool->params.bpid); -+ kfree(pool->sp); -+ pool->sp = NULL; -+ pool->params.flags ^= BMAN_POOL_FLAG_STOCKPILE; -+ } -+ if (pool->params.flags & BMAN_POOL_FLAG_DYNAMIC_BPID) -+ bman_release_bpid(pool->params.bpid); -+ kfree(pool); -+} -+EXPORT_SYMBOL(bman_free_pool); -+ -+const struct bman_pool_params *bman_get_params(const struct bman_pool *pool) -+{ -+ return &pool->params; -+} -+EXPORT_SYMBOL(bman_get_params); -+ -+static noinline void update_rcr_ci(struct bman_portal *p, u8 avail) -+{ -+ if (avail) -+ bm_rcr_cce_prefetch(&p->p); -+ else -+ bm_rcr_cce_update(&p->p); -+} -+ -+int bman_rcr_is_empty(void) -+{ -+ __maybe_unused unsigned long irqflags; -+ struct bman_portal *p = get_affine_portal(); -+ u8 avail; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ update_rcr_ci(p, 0); -+ avail = bm_rcr_get_fill(&p->p); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return avail == 0; -+} -+EXPORT_SYMBOL(bman_rcr_is_empty); -+ -+static inline struct bm_rcr_entry *try_rel_start(struct bman_portal **p, -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ __maybe_unused struct bman_pool *pool, -+#endif -+ __maybe_unused unsigned long *irqflags, -+ __maybe_unused u32 flags) -+{ -+ struct bm_rcr_entry *r; -+ u8 avail; -+ -+ *p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(*p, (*irqflags)); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & BMAN_RELEASE_FLAG_WAIT) && -+ (flags & BMAN_RELEASE_FLAG_WAIT_SYNC))) { -+ if ((*p)->rcri_owned) { -+ PORTAL_IRQ_UNLOCK(*p, (*irqflags)); -+ put_affine_portal(); -+ return NULL; -+ } -+ (*p)->rcri_owned = pool; -+ } -+#endif -+ avail = bm_rcr_get_avail(&(*p)->p); -+ if (avail < 2) -+ update_rcr_ci(*p, avail); -+ r = bm_rcr_start(&(*p)->p); -+ if (unlikely(!r)) { -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & BMAN_RELEASE_FLAG_WAIT) && -+ (flags & BMAN_RELEASE_FLAG_WAIT_SYNC))) -+ (*p)->rcri_owned = NULL; -+#endif -+ PORTAL_IRQ_UNLOCK(*p, (*irqflags)); -+ put_affine_portal(); -+ } -+ return r; -+} -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+static noinline struct bm_rcr_entry *__wait_rel_start(struct bman_portal **p, -+ struct bman_pool *pool, -+ __maybe_unused unsigned long *irqflags, -+ u32 flags) -+{ -+ struct bm_rcr_entry *rcr = try_rel_start(p, pool, irqflags, flags); -+ if (!rcr) -+ bm_rcr_set_ithresh(&(*p)->p, 1); -+ return rcr; -+} -+ -+static noinline struct bm_rcr_entry *wait_rel_start(struct bman_portal **p, -+ struct bman_pool *pool, -+ __maybe_unused unsigned long *irqflags, -+ u32 flags) -+{ -+ struct bm_rcr_entry *rcr; -+#ifndef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ pool = NULL; -+#endif -+ if (flags & BMAN_RELEASE_FLAG_WAIT_INT) -+ /* NB: return NULL if signal occurs before completion. Signal -+ * can occur during return. Caller must check for signal */ -+ wait_event_interruptible(affine_queue, -+ (rcr = __wait_rel_start(p, pool, irqflags, flags))); -+ else -+ wait_event(affine_queue, -+ (rcr = __wait_rel_start(p, pool, irqflags, flags))); -+ return rcr; -+} -+#endif -+ -+static inline int __bman_release(struct bman_pool *pool, -+ const struct bm_buffer *bufs, u8 num, u32 flags) -+{ -+ struct bman_portal *p; -+ struct bm_rcr_entry *r; -+ __maybe_unused unsigned long irqflags; -+ u32 i = num - 1; -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & BMAN_RELEASE_FLAG_WAIT) -+ r = wait_rel_start(&p, pool, &irqflags, flags); -+ else -+ r = try_rel_start(&p, pool, &irqflags, flags); -+#else -+ r = try_rel_start(&p, &irqflags, flags); -+#endif -+ if (!r) -+ return -EBUSY; -+ /* We can copy all but the first entry, as this can trigger badness -+ * with the valid-bit. Use the overlay to mask the verb byte. */ -+ r->bufs[0].opaque = -+ ((cpu_to_be64((bufs[0].opaque | -+ ((u64)pool->params.bpid<<48)) -+ & 0x00ffffffffffffff))); -+ if (i) { -+ for (i = 1; i < num; i++) -+ r->bufs[i].opaque = -+ cpu_to_be64(bufs[i].opaque); -+ } -+ -+ bm_rcr_pvb_commit(&p->p, BM_RCR_VERB_CMD_BPID_SINGLE | -+ (num & BM_RCR_VERB_BUFCOUNT_MASK)); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ /* if we wish to sync we need to set the threshold after h/w sees the -+ * new ring entry. As we're mixing cache-enabled and cache-inhibited -+ * accesses, this requires a heavy-weight sync. */ -+ if (unlikely((flags & BMAN_RELEASE_FLAG_WAIT) && -+ (flags & BMAN_RELEASE_FLAG_WAIT_SYNC))) { -+ hwsync(); -+ bm_rcr_set_ithresh(&p->p, 1); -+ } -+#endif -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & BMAN_RELEASE_FLAG_WAIT) && -+ (flags & BMAN_RELEASE_FLAG_WAIT_SYNC))) { -+ if (flags & BMAN_RELEASE_FLAG_WAIT_INT) -+ /* NB: return success even if signal occurs before -+ * condition is true. pvb_commit guarantees success */ -+ wait_event_interruptible(affine_queue, -+ (p->rcri_owned != pool)); -+ else -+ wait_event(affine_queue, (p->rcri_owned != pool)); -+ } -+#endif -+ return 0; -+} -+ -+int bman_release(struct bman_pool *pool, const struct bm_buffer *bufs, u8 num, -+ u32 flags) -+{ -+ int ret; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (!num || (num > 8)) -+ return -EINVAL; -+ if (pool->params.flags & BMAN_POOL_FLAG_NO_RELEASE) -+ return -EINVAL; -+#endif -+ /* Without stockpile, this API is a pass-through to the h/w operation */ -+ if (!(pool->params.flags & BMAN_POOL_FLAG_STOCKPILE)) -+ return __bman_release(pool, bufs, num, flags); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (!atomic_dec_and_test(&pool->in_use)) { -+ pr_crit("Parallel attempts to enter bman_released() detected."); -+ panic("only one instance of bman_released/acquired allowed"); -+ } -+#endif -+ /* Two movements of buffers are possible, and can occur in either order. -+ * A: moving buffers from the caller to the stockpile. -+ * B: moving buffers from the stockpile to hardware. -+ * Order 1: if there is already enough space in the stockpile for A -+ * then we want to do A first, and only do B if we trigger the -+ * stockpile-high threshold. -+ * Order 2: if there is not enough space in the stockpile for A, then -+ * we want to do B first, then do A if B had succeeded. However in this -+ * case B is dependent on how many buffers the user needs to release, -+ * not the stockpile-high threshold. -+ * Due to the different handling of B between the two cases, putting A -+ * and B in a while() loop would require quite obscure logic, so handle -+ * the different sequences explicitly. */ -+ if ((pool->sp_fill + num) <= BMAN_STOCKPILE_SZ) { -+ /* Order 1: do A */ -+ copy_words(pool->sp + pool->sp_fill, bufs, -+ sizeof(struct bm_buffer) * num); -+ pool->sp_fill += num; -+ /* do B relative to STOCKPILE_HIGH */ -+ while (pool->sp_fill >= BMAN_STOCKPILE_HIGH) { -+ ret = __bman_release(pool, -+ pool->sp + (pool->sp_fill - 8), 8, -+ flags); -+ if (ret >= 0) -+ pool->sp_fill -= 8; -+ } -+ } else { -+ /* Order 2: do B relative to 'num' */ -+ do { -+ ret = __bman_release(pool, -+ pool->sp + (pool->sp_fill - 8), 8, -+ flags); -+ if (ret < 0) -+ /* failure */ -+ goto release_done; -+ pool->sp_fill -= 8; -+ } while ((pool->sp_fill + num) > BMAN_STOCKPILE_SZ); -+ /* do A */ -+ copy_words(pool->sp + pool->sp_fill, bufs, -+ sizeof(struct bm_buffer) * num); -+ pool->sp_fill += num; -+ } -+ /* success */ -+ ret = 0; -+release_done: -+#ifdef CONFIG_FSL_DPA_CHECKING -+ atomic_inc(&pool->in_use); -+#endif -+ return ret; -+} -+EXPORT_SYMBOL(bman_release); -+ -+static inline int __bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, -+ u8 num) -+{ -+ struct bman_portal *p = get_affine_portal(); -+ struct bm_mc_command *mcc; -+ struct bm_mc_result *mcr; -+ __maybe_unused unsigned long irqflags; -+ int ret, i; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ mcc = bm_mc_start(&p->p); -+ mcc->acquire.bpid = pool->params.bpid; -+ bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE | -+ (num & BM_MCC_VERB_ACQUIRE_BUFCOUNT)); -+ while (!(mcr = bm_mc_result(&p->p))) -+ cpu_relax(); -+ ret = mcr->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT; -+ if (bufs) { -+ for (i = 0; i < num; i++) -+ bufs[i].opaque = -+ be64_to_cpu(mcr->acquire.bufs[i].opaque); -+ } -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (ret != num) -+ ret = -ENOMEM; -+ return ret; -+} -+ -+int bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, u8 num, -+ u32 flags) -+{ -+ int ret; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (!num || (num > 8)) -+ return -EINVAL; -+ if (pool->params.flags & BMAN_POOL_FLAG_ONLY_RELEASE) -+ return -EINVAL; -+#endif -+ /* Without stockpile, this API is a pass-through to the h/w operation */ -+ if (!(pool->params.flags & BMAN_POOL_FLAG_STOCKPILE)) -+ return __bman_acquire(pool, bufs, num); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (!atomic_dec_and_test(&pool->in_use)) { -+ pr_crit("Parallel attempts to enter bman_acquire() detected."); -+ panic("only one instance of bman_released/acquired allowed"); -+ } -+#endif -+ /* Two movements of buffers are possible, and can occur in either order. -+ * A: moving buffers from stockpile to the caller. -+ * B: moving buffers from hardware to the stockpile. -+ * Order 1: if there are already enough buffers in the stockpile for A -+ * then we want to do A first, and only do B if we trigger the -+ * stockpile-low threshold. -+ * Order 2: if there are not enough buffers in the stockpile for A, -+ * then we want to do B first, then do A if B had succeeded. However in -+ * this case B is dependent on how many buffers the user needs, not the -+ * stockpile-low threshold. -+ * Due to the different handling of B between the two cases, putting A -+ * and B in a while() loop would require quite obscure logic, so handle -+ * the different sequences explicitly. */ -+ if (num <= pool->sp_fill) { -+ /* Order 1: do A */ -+ copy_words(bufs, pool->sp + (pool->sp_fill - num), -+ sizeof(struct bm_buffer) * num); -+ pool->sp_fill -= num; -+ /* do B relative to STOCKPILE_LOW */ -+ while (pool->sp_fill <= BMAN_STOCKPILE_LOW) { -+ ret = __bman_acquire(pool, pool->sp + pool->sp_fill, 8); -+ if (ret < 0) -+ ret = __bman_acquire(pool, -+ pool->sp + pool->sp_fill, 1); -+ if (ret < 0) -+ break; -+ pool->sp_fill += ret; -+ } -+ } else { -+ /* Order 2: do B relative to 'num' */ -+ do { -+ ret = __bman_acquire(pool, pool->sp + pool->sp_fill, 8); -+ if (ret < 0) -+ ret = __bman_acquire(pool, -+ pool->sp + pool->sp_fill, 1); -+ if (ret < 0) -+ /* failure */ -+ goto acquire_done; -+ pool->sp_fill += ret; -+ } while (pool->sp_fill < num); -+ /* do A */ -+ copy_words(bufs, pool->sp + (pool->sp_fill - num), -+ sizeof(struct bm_buffer) * num); -+ pool->sp_fill -= num; -+ } -+ /* success */ -+ ret = num; -+acquire_done: -+#ifdef CONFIG_FSL_DPA_CHECKING -+ atomic_inc(&pool->in_use); -+#endif -+ return ret; -+} -+EXPORT_SYMBOL(bman_acquire); -+ -+int bman_flush_stockpile(struct bman_pool *pool, u32 flags) -+{ -+ u8 num; -+ int ret; -+ -+ while (pool->sp_fill) { -+ num = ((pool->sp_fill > 8) ? 8 : pool->sp_fill); -+ ret = __bman_release(pool, pool->sp + (pool->sp_fill - num), -+ num, flags); -+ if (ret) -+ return ret; -+ pool->sp_fill -= num; -+ } -+ return 0; -+} -+EXPORT_SYMBOL(bman_flush_stockpile); -+ -+int bman_query_pools(struct bm_pool_state *state) -+{ -+ struct bman_portal *p = get_affine_portal(); -+ struct bm_mc_result *mcr; -+ __maybe_unused unsigned long irqflags; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ bm_mc_start(&p->p); -+ bm_mc_commit(&p->p, BM_MCC_VERB_CMD_QUERY); -+ while (!(mcr = bm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & BM_MCR_VERB_CMD_MASK) == BM_MCR_VERB_CMD_QUERY); -+ *state = mcr->query; -+ state->as.state.__state[0] = be32_to_cpu(state->as.state.__state[0]); -+ state->as.state.__state[1] = be32_to_cpu(state->as.state.__state[1]); -+ state->ds.state.__state[0] = be32_to_cpu(state->ds.state.__state[0]); -+ state->ds.state.__state[1] = be32_to_cpu(state->ds.state.__state[1]); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return 0; -+} -+EXPORT_SYMBOL(bman_query_pools); -+ -+#ifdef CONFIG_FSL_BMAN_CONFIG -+u32 bman_query_free_buffers(struct bman_pool *pool) -+{ -+ return bm_pool_free_buffers(pool->params.bpid); -+} -+EXPORT_SYMBOL(bman_query_free_buffers); -+ -+int bman_update_pool_thresholds(struct bman_pool *pool, const u32 *thresholds) -+{ -+ u32 bpid; -+ -+ bpid = bman_get_params(pool)->bpid; -+ -+ return bm_pool_set(bpid, thresholds); -+} -+EXPORT_SYMBOL(bman_update_pool_thresholds); -+#endif -+ -+int bman_shutdown_pool(u32 bpid) -+{ -+ struct bman_portal *p = get_affine_portal(); -+ __maybe_unused unsigned long irqflags; -+ int ret; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ ret = bm_shutdown_pool(&p->p, bpid); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(bman_shutdown_pool); -+ -+const struct bm_portal_config *bman_get_bm_portal_config( -+ struct bman_portal *portal) -+{ -+ return portal->sharing_redirect ? NULL : portal->config; -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_low.h -@@ -0,0 +1,565 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "bman_private.h" -+ -+/***************************/ -+/* Portal register assists */ -+/***************************/ -+ -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ -+/* Cache-inhibited register offsets */ -+#define BM_REG_RCR_PI_CINH 0x0000 -+#define BM_REG_RCR_CI_CINH 0x0004 -+#define BM_REG_RCR_ITR 0x0008 -+#define BM_REG_CFG 0x0100 -+#define BM_REG_SCN(n) (0x0200 + ((n) << 2)) -+#define BM_REG_ISR 0x0e00 -+#define BM_REG_IIR 0x0e0c -+ -+/* Cache-enabled register offsets */ -+#define BM_CL_CR 0x0000 -+#define BM_CL_RR0 0x0100 -+#define BM_CL_RR1 0x0140 -+#define BM_CL_RCR 0x1000 -+#define BM_CL_RCR_PI_CENA 0x3000 -+#define BM_CL_RCR_CI_CENA 0x3100 -+ -+#endif -+ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ -+/* Cache-inhibited register offsets */ -+#define BM_REG_RCR_PI_CINH 0x3000 -+#define BM_REG_RCR_CI_CINH 0x3100 -+#define BM_REG_RCR_ITR 0x3200 -+#define BM_REG_CFG 0x3300 -+#define BM_REG_SCN(n) (0x3400 + ((n) << 6)) -+#define BM_REG_ISR 0x3e00 -+#define BM_REG_IIR 0x3ec0 -+ -+/* Cache-enabled register offsets */ -+#define BM_CL_CR 0x0000 -+#define BM_CL_RR0 0x0100 -+#define BM_CL_RR1 0x0140 -+#define BM_CL_RCR 0x1000 -+#define BM_CL_RCR_PI_CENA 0x3000 -+#define BM_CL_RCR_CI_CENA 0x3100 -+ -+#endif -+ -+/* BTW, the drivers (and h/w programming model) already obtain the required -+ * synchronisation for portal accesses via lwsync(), hwsync(), and -+ * data-dependencies. Use of barrier()s or other order-preserving primitives -+ * simply degrade performance. Hence the use of the __raw_*() interfaces, which -+ * simply ensure that the compiler treats the portal registers as volatile (ie. -+ * non-coherent). */ -+ -+/* Cache-inhibited register access. */ -+#define __bm_in(bm, o) be32_to_cpu(__raw_readl((bm)->addr_ci + (o))) -+#define __bm_out(bm, o, val) __raw_writel(cpu_to_be32(val), \ -+ (bm)->addr_ci + (o)); -+#define bm_in(reg) __bm_in(&portal->addr, BM_REG_##reg) -+#define bm_out(reg, val) __bm_out(&portal->addr, BM_REG_##reg, val) -+ -+/* Cache-enabled (index) register access */ -+#define __bm_cl_touch_ro(bm, o) dcbt_ro((bm)->addr_ce + (o)) -+#define __bm_cl_touch_rw(bm, o) dcbt_rw((bm)->addr_ce + (o)) -+#define __bm_cl_in(bm, o) be32_to_cpu(__raw_readl((bm)->addr_ce + (o))) -+#define __bm_cl_out(bm, o, val) \ -+ do { \ -+ u32 *__tmpclout = (bm)->addr_ce + (o); \ -+ __raw_writel(cpu_to_be32(val), __tmpclout); \ -+ dcbf(__tmpclout); \ -+ } while (0) -+#define __bm_cl_invalidate(bm, o) dcbi((bm)->addr_ce + (o)) -+#define bm_cl_touch_ro(reg) __bm_cl_touch_ro(&portal->addr, BM_CL_##reg##_CENA) -+#define bm_cl_touch_rw(reg) __bm_cl_touch_rw(&portal->addr, BM_CL_##reg##_CENA) -+#define bm_cl_in(reg) __bm_cl_in(&portal->addr, BM_CL_##reg##_CENA) -+#define bm_cl_out(reg, val) __bm_cl_out(&portal->addr, BM_CL_##reg##_CENA, val) -+#define bm_cl_invalidate(reg)\ -+ __bm_cl_invalidate(&portal->addr, BM_CL_##reg##_CENA) -+ -+/* Cyclic helper for rings. FIXME: once we are able to do fine-grain perf -+ * analysis, look at using the "extra" bit in the ring index registers to avoid -+ * cyclic issues. */ -+static inline u8 bm_cyc_diff(u8 ringsize, u8 first, u8 last) -+{ -+ /* 'first' is included, 'last' is excluded */ -+ if (first <= last) -+ return last - first; -+ return ringsize + last - first; -+} -+ -+/* Portal modes. -+ * Enum types; -+ * pmode == production mode -+ * cmode == consumption mode, -+ * Enum values use 3 letter codes. First letter matches the portal mode, -+ * remaining two letters indicate; -+ * ci == cache-inhibited portal register -+ * ce == cache-enabled portal register -+ * vb == in-band valid-bit (cache-enabled) -+ */ -+enum bm_rcr_pmode { /* matches BCSP_CFG::RPM */ -+ bm_rcr_pci = 0, /* PI index, cache-inhibited */ -+ bm_rcr_pce = 1, /* PI index, cache-enabled */ -+ bm_rcr_pvb = 2 /* valid-bit */ -+}; -+enum bm_rcr_cmode { /* s/w-only */ -+ bm_rcr_cci, /* CI index, cache-inhibited */ -+ bm_rcr_cce /* CI index, cache-enabled */ -+}; -+ -+ -+/* ------------------------- */ -+/* --- Portal structures --- */ -+ -+#define BM_RCR_SIZE 8 -+ -+struct bm_rcr { -+ struct bm_rcr_entry *ring, *cursor; -+ u8 ci, available, ithresh, vbit; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ u32 busy; -+ enum bm_rcr_pmode pmode; -+ enum bm_rcr_cmode cmode; -+#endif -+}; -+ -+struct bm_mc { -+ struct bm_mc_command *cr; -+ struct bm_mc_result *rr; -+ u8 rridx, vbit; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ enum { -+ /* Can only be _mc_start()ed */ -+ mc_idle, -+ /* Can only be _mc_commit()ed or _mc_abort()ed */ -+ mc_user, -+ /* Can only be _mc_retry()ed */ -+ mc_hw -+ } state; -+#endif -+}; -+ -+struct bm_addr { -+ void __iomem *addr_ce; /* cache-enabled */ -+ void __iomem *addr_ci; /* cache-inhibited */ -+}; -+ -+struct bm_portal { -+ struct bm_addr addr; -+ struct bm_rcr rcr; -+ struct bm_mc mc; -+ struct bm_portal_config config; -+} ____cacheline_aligned; -+ -+ -+/* --------------- */ -+/* --- RCR API --- */ -+ -+/* Bit-wise logic to wrap a ring pointer by clearing the "carry bit" */ -+#define RCR_CARRYCLEAR(p) \ -+ (void *)((unsigned long)(p) & (~(unsigned long)(BM_RCR_SIZE << 6))) -+ -+/* Bit-wise logic to convert a ring pointer to a ring index */ -+static inline u8 RCR_PTR2IDX(struct bm_rcr_entry *e) -+{ -+ return ((uintptr_t)e >> 6) & (BM_RCR_SIZE - 1); -+} -+ -+/* Increment the 'cursor' ring pointer, taking 'vbit' into account */ -+static inline void RCR_INC(struct bm_rcr *rcr) -+{ -+ /* NB: this is odd-looking, but experiments show that it generates -+ * fast code with essentially no branching overheads. We increment to -+ * the next RCR pointer and handle overflow and 'vbit'. */ -+ struct bm_rcr_entry *partial = rcr->cursor + 1; -+ rcr->cursor = RCR_CARRYCLEAR(partial); -+ if (partial != rcr->cursor) -+ rcr->vbit ^= BM_RCR_VERB_VBIT; -+} -+ -+static inline int bm_rcr_init(struct bm_portal *portal, enum bm_rcr_pmode pmode, -+ __maybe_unused enum bm_rcr_cmode cmode) -+{ -+ /* This use of 'register', as well as all other occurrences, is because -+ * it has been observed to generate much faster code with gcc than is -+ * otherwise the case. */ -+ register struct bm_rcr *rcr = &portal->rcr; -+ u32 cfg; -+ u8 pi; -+ -+ rcr->ring = portal->addr.addr_ce + BM_CL_RCR; -+ rcr->ci = bm_in(RCR_CI_CINH) & (BM_RCR_SIZE - 1); -+ -+ pi = bm_in(RCR_PI_CINH) & (BM_RCR_SIZE - 1); -+ rcr->cursor = rcr->ring + pi; -+ rcr->vbit = (bm_in(RCR_PI_CINH) & BM_RCR_SIZE) ? BM_RCR_VERB_VBIT : 0; -+ rcr->available = BM_RCR_SIZE - 1 -+ - bm_cyc_diff(BM_RCR_SIZE, rcr->ci, pi); -+ rcr->ithresh = bm_in(RCR_ITR); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ rcr->busy = 0; -+ rcr->pmode = pmode; -+ rcr->cmode = cmode; -+#endif -+ cfg = (bm_in(CFG) & 0xffffffe0) | (pmode & 0x3); /* BCSP_CFG::RPM */ -+ bm_out(CFG, cfg); -+ return 0; -+} -+ -+static inline void bm_rcr_finish(struct bm_portal *portal) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ u8 pi = bm_in(RCR_PI_CINH) & (BM_RCR_SIZE - 1); -+ u8 ci = bm_in(RCR_CI_CINH) & (BM_RCR_SIZE - 1); -+ DPA_ASSERT(!rcr->busy); -+ if (pi != RCR_PTR2IDX(rcr->cursor)) -+ pr_crit("losing uncommited RCR entries\n"); -+ if (ci != rcr->ci) -+ pr_crit("missing existing RCR completions\n"); -+ if (rcr->ci != RCR_PTR2IDX(rcr->cursor)) -+ pr_crit("RCR destroyed unquiesced\n"); -+} -+ -+static inline struct bm_rcr_entry *bm_rcr_start(struct bm_portal *portal) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ DPA_ASSERT(!rcr->busy); -+ if (!rcr->available) -+ return NULL; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ rcr->busy = 1; -+#endif -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ dcbz_64(rcr->cursor); -+#endif -+ return rcr->cursor; -+} -+ -+static inline void bm_rcr_abort(struct bm_portal *portal) -+{ -+ __maybe_unused register struct bm_rcr *rcr = &portal->rcr; -+ DPA_ASSERT(rcr->busy); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ rcr->busy = 0; -+#endif -+} -+ -+static inline struct bm_rcr_entry *bm_rcr_pend_and_next( -+ struct bm_portal *portal, u8 myverb) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ DPA_ASSERT(rcr->busy); -+ DPA_ASSERT(rcr->pmode != bm_rcr_pvb); -+ if (rcr->available == 1) -+ return NULL; -+ rcr->cursor->__dont_write_directly__verb = myverb | rcr->vbit; -+ dcbf_64(rcr->cursor); -+ RCR_INC(rcr); -+ rcr->available--; -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ dcbz_64(rcr->cursor); -+#endif -+ return rcr->cursor; -+} -+ -+static inline void bm_rcr_pci_commit(struct bm_portal *portal, u8 myverb) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ DPA_ASSERT(rcr->busy); -+ DPA_ASSERT(rcr->pmode == bm_rcr_pci); -+ rcr->cursor->__dont_write_directly__verb = myverb | rcr->vbit; -+ RCR_INC(rcr); -+ rcr->available--; -+ hwsync(); -+ bm_out(RCR_PI_CINH, RCR_PTR2IDX(rcr->cursor)); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ rcr->busy = 0; -+#endif -+} -+ -+static inline void bm_rcr_pce_prefetch(struct bm_portal *portal) -+{ -+ __maybe_unused register struct bm_rcr *rcr = &portal->rcr; -+ DPA_ASSERT(rcr->pmode == bm_rcr_pce); -+ bm_cl_invalidate(RCR_PI); -+ bm_cl_touch_rw(RCR_PI); -+} -+ -+static inline void bm_rcr_pce_commit(struct bm_portal *portal, u8 myverb) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ DPA_ASSERT(rcr->busy); -+ DPA_ASSERT(rcr->pmode == bm_rcr_pce); -+ rcr->cursor->__dont_write_directly__verb = myverb | rcr->vbit; -+ RCR_INC(rcr); -+ rcr->available--; -+ lwsync(); -+ bm_cl_out(RCR_PI, RCR_PTR2IDX(rcr->cursor)); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ rcr->busy = 0; -+#endif -+} -+ -+static inline void bm_rcr_pvb_commit(struct bm_portal *portal, u8 myverb) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ struct bm_rcr_entry *rcursor; -+ DPA_ASSERT(rcr->busy); -+ DPA_ASSERT(rcr->pmode == bm_rcr_pvb); -+ lwsync(); -+ rcursor = rcr->cursor; -+ rcursor->__dont_write_directly__verb = myverb | rcr->vbit; -+ dcbf_64(rcursor); -+ RCR_INC(rcr); -+ rcr->available--; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ rcr->busy = 0; -+#endif -+} -+ -+static inline u8 bm_rcr_cci_update(struct bm_portal *portal) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ u8 diff, old_ci = rcr->ci; -+ DPA_ASSERT(rcr->cmode == bm_rcr_cci); -+ rcr->ci = bm_in(RCR_CI_CINH) & (BM_RCR_SIZE - 1); -+ diff = bm_cyc_diff(BM_RCR_SIZE, old_ci, rcr->ci); -+ rcr->available += diff; -+ return diff; -+} -+ -+static inline void bm_rcr_cce_prefetch(struct bm_portal *portal) -+{ -+ __maybe_unused register struct bm_rcr *rcr = &portal->rcr; -+ DPA_ASSERT(rcr->cmode == bm_rcr_cce); -+ bm_cl_touch_ro(RCR_CI); -+} -+ -+static inline u8 bm_rcr_cce_update(struct bm_portal *portal) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ u8 diff, old_ci = rcr->ci; -+ DPA_ASSERT(rcr->cmode == bm_rcr_cce); -+ rcr->ci = bm_cl_in(RCR_CI) & (BM_RCR_SIZE - 1); -+ bm_cl_invalidate(RCR_CI); -+ diff = bm_cyc_diff(BM_RCR_SIZE, old_ci, rcr->ci); -+ rcr->available += diff; -+ return diff; -+} -+ -+static inline u8 bm_rcr_get_ithresh(struct bm_portal *portal) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ return rcr->ithresh; -+} -+ -+static inline void bm_rcr_set_ithresh(struct bm_portal *portal, u8 ithresh) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ rcr->ithresh = ithresh; -+ bm_out(RCR_ITR, ithresh); -+} -+ -+static inline u8 bm_rcr_get_avail(struct bm_portal *portal) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ return rcr->available; -+} -+ -+static inline u8 bm_rcr_get_fill(struct bm_portal *portal) -+{ -+ register struct bm_rcr *rcr = &portal->rcr; -+ return BM_RCR_SIZE - 1 - rcr->available; -+} -+ -+ -+/* ------------------------------ */ -+/* --- Management command API --- */ -+ -+static inline int bm_mc_init(struct bm_portal *portal) -+{ -+ register struct bm_mc *mc = &portal->mc; -+ mc->cr = portal->addr.addr_ce + BM_CL_CR; -+ mc->rr = portal->addr.addr_ce + BM_CL_RR0; -+ mc->rridx = (__raw_readb(&mc->cr->__dont_write_directly__verb) & -+ BM_MCC_VERB_VBIT) ? 0 : 1; -+ mc->vbit = mc->rridx ? BM_MCC_VERB_VBIT : 0; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = mc_idle; -+#endif -+ return 0; -+} -+ -+static inline void bm_mc_finish(struct bm_portal *portal) -+{ -+ __maybe_unused register struct bm_mc *mc = &portal->mc; -+ DPA_ASSERT(mc->state == mc_idle); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (mc->state != mc_idle) -+ pr_crit("Losing incomplete MC command\n"); -+#endif -+} -+ -+static inline struct bm_mc_command *bm_mc_start(struct bm_portal *portal) -+{ -+ register struct bm_mc *mc = &portal->mc; -+ DPA_ASSERT(mc->state == mc_idle); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = mc_user; -+#endif -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ dcbz_64(mc->cr); -+#endif -+ return mc->cr; -+} -+ -+static inline void bm_mc_abort(struct bm_portal *portal) -+{ -+ __maybe_unused register struct bm_mc *mc = &portal->mc; -+ DPA_ASSERT(mc->state == mc_user); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = mc_idle; -+#endif -+} -+ -+static inline void bm_mc_commit(struct bm_portal *portal, u8 myverb) -+{ -+ register struct bm_mc *mc = &portal->mc; -+ struct bm_mc_result *rr = mc->rr + mc->rridx; -+ DPA_ASSERT(mc->state == mc_user); -+ lwsync(); -+ mc->cr->__dont_write_directly__verb = myverb | mc->vbit; -+ dcbf(mc->cr); -+ dcbit_ro(rr); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = mc_hw; -+#endif -+} -+ -+static inline struct bm_mc_result *bm_mc_result(struct bm_portal *portal) -+{ -+ register struct bm_mc *mc = &portal->mc; -+ struct bm_mc_result *rr = mc->rr + mc->rridx; -+ DPA_ASSERT(mc->state == mc_hw); -+ /* The inactive response register's verb byte always returns zero until -+ * its command is submitted and completed. This includes the valid-bit, -+ * in case you were wondering... */ -+ if (!__raw_readb(&rr->verb)) { -+ dcbit_ro(rr); -+ return NULL; -+ } -+ mc->rridx ^= 1; -+ mc->vbit ^= BM_MCC_VERB_VBIT; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = mc_idle; -+#endif -+ return rr; -+} -+ -+ -+/* ------------------------------------- */ -+/* --- Portal interrupt register API --- */ -+ -+static inline int bm_isr_init(__always_unused struct bm_portal *portal) -+{ -+ return 0; -+} -+ -+static inline void bm_isr_finish(__always_unused struct bm_portal *portal) -+{ -+} -+ -+#define SCN_REG(bpid) BM_REG_SCN((bpid) / 32) -+#define SCN_BIT(bpid) (0x80000000 >> (bpid & 31)) -+static inline void bm_isr_bscn_mask(struct bm_portal *portal, u8 bpid, -+ int enable) -+{ -+ u32 val; -+ DPA_ASSERT(bpid < bman_pool_max); -+ /* REG_SCN for bpid=0..31, REG_SCN+4 for bpid=32..63 */ -+ val = __bm_in(&portal->addr, SCN_REG(bpid)); -+ if (enable) -+ val |= SCN_BIT(bpid); -+ else -+ val &= ~SCN_BIT(bpid); -+ __bm_out(&portal->addr, SCN_REG(bpid), val); -+} -+ -+static inline u32 __bm_isr_read(struct bm_portal *portal, enum bm_isr_reg n) -+{ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ return __bm_in(&portal->addr, BM_REG_ISR + (n << 6)); -+#else -+ return __bm_in(&portal->addr, BM_REG_ISR + (n << 2)); -+#endif -+} -+ -+static inline void __bm_isr_write(struct bm_portal *portal, enum bm_isr_reg n, -+ u32 val) -+{ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ __bm_out(&portal->addr, BM_REG_ISR + (n << 6), val); -+#else -+ __bm_out(&portal->addr, BM_REG_ISR + (n << 2), val); -+#endif -+} -+ -+/* Buffer Pool Cleanup */ -+static inline int bm_shutdown_pool(struct bm_portal *p, u32 bpid) -+{ -+ struct bm_mc_command *bm_cmd; -+ struct bm_mc_result *bm_res; -+ -+ int aq_count = 0; -+ bool stop = false; -+ while (!stop) { -+ /* Acquire buffers until empty */ -+ bm_cmd = bm_mc_start(p); -+ bm_cmd->acquire.bpid = bpid; -+ bm_mc_commit(p, BM_MCC_VERB_CMD_ACQUIRE | 1); -+ while (!(bm_res = bm_mc_result(p))) -+ cpu_relax(); -+ if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) { -+ /* Pool is empty */ -+ /* TBD : Should we do a few extra iterations in -+ case some other some blocks keep buffers 'on deck', -+ which may also be problematic */ -+ stop = true; -+ } else -+ ++aq_count; -+ } -+ return 0; -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_private.h -@@ -0,0 +1,166 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "dpa_sys.h" -+#include <linux/fsl_bman.h> -+ -+/* Revision info (for errata and feature handling) */ -+#define BMAN_REV10 0x0100 -+#define BMAN_REV20 0x0200 -+#define BMAN_REV21 0x0201 -+#define QBMAN_ANY_PORTAL_IDX 0xffffffff -+extern u16 bman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */ -+ -+/* -+ * Global variables of the max portal/pool number this bman version supported -+ */ -+extern u16 bman_pool_max; -+ -+/* used by CCSR and portal interrupt code */ -+enum bm_isr_reg { -+ bm_isr_status = 0, -+ bm_isr_enable = 1, -+ bm_isr_disable = 2, -+ bm_isr_inhibit = 3 -+}; -+ -+struct bm_portal_config { -+ /* Corenet portal addresses; -+ * [0]==cache-enabled, [1]==cache-inhibited. */ -+ __iomem void *addr_virt[2]; -+ struct resource addr_phys[2]; -+ /* Allow these to be joined in lists */ -+ struct list_head list; -+ /* User-visible portal configuration settings */ -+ struct bman_portal_config public_cfg; -+ /* power management saved data */ -+ u32 saved_isdr; -+}; -+ -+#ifdef CONFIG_FSL_BMAN_CONFIG -+/* Hooks from bman_driver.c to bman_config.c */ -+int bman_init_ccsr(struct device_node *node); -+#endif -+ -+/* Hooks from bman_driver.c in to bman_high.c */ -+struct bman_portal *bman_create_portal( -+ struct bman_portal *portal, -+ const struct bm_portal_config *config); -+struct bman_portal *bman_create_affine_portal( -+ const struct bm_portal_config *config); -+struct bman_portal *bman_create_affine_slave(struct bman_portal *redirect, -+ int cpu); -+void bman_destroy_portal(struct bman_portal *bm); -+ -+const struct bm_portal_config *bman_destroy_affine_portal(void); -+ -+/* Hooks from fsl_usdpaa.c to bman_driver.c */ -+struct bm_portal_config *bm_get_unused_portal(void); -+struct bm_portal_config *bm_get_unused_portal_idx(uint32_t idx); -+void bm_put_unused_portal(struct bm_portal_config *pcfg); -+void bm_set_liodns(struct bm_portal_config *pcfg); -+ -+/* Pool logic in the portal driver, during initialisation, needs to know if -+ * there's access to CCSR or not (if not, it'll cripple the pool allocator). */ -+#ifdef CONFIG_FSL_BMAN_CONFIG -+int bman_have_ccsr(void); -+#else -+#define bman_have_ccsr() 0 -+#endif -+ -+/* Stockpile build constants. The _LOW value: when bman_acquire() is called and -+ * the stockpile fill-level is <= _LOW, an acquire is attempted from h/w but it -+ * might fail (if the buffer pool is depleted). So this value provides some -+ * "stagger" in that the bman_acquire() function will only fail if lots of bufs -+ * are requested at once or if h/w has been tested a couple of times without -+ * luck. The _HIGH value: when bman_release() is called and the stockpile -+ * fill-level is >= _HIGH, a release is attempted to h/w but it might fail (if -+ * the release ring is full). So this value provides some "stagger" so that -+ * ring-access is retried a couple of times prior to the API returning a -+ * failure. The following *must* be true; -+ * BMAN_STOCKPILE_HIGH-BMAN_STOCKPILE_LOW > 8 -+ * (to avoid thrashing) -+ * BMAN_STOCKPILE_SZ >= 16 -+ * (as the release logic expects to either send 8 buffers to hw prior to -+ * adding the given buffers to the stockpile or add the buffers to the -+ * stockpile before sending 8 to hw, as the API must be an all-or-nothing -+ * success/fail.) -+ */ -+#define BMAN_STOCKPILE_SZ 16u /* number of bufs in per-pool cache */ -+#define BMAN_STOCKPILE_LOW 2u /* when fill is <= this, acquire from hw */ -+#define BMAN_STOCKPILE_HIGH 14u /* when fill is >= this, release to hw */ -+ -+/*************************************************/ -+/* BMan s/w corenet portal, low-level i/face */ -+/*************************************************/ -+ -+/* Used by all portal interrupt registers except 'inhibit' -+ * This mask contains all the "irqsource" bits visible to API users -+ */ -+#define BM_PIRQ_VISIBLE (BM_PIRQ_RCRI | BM_PIRQ_BSCN) -+ -+/* These are bm_<reg>_<verb>(). So for example, bm_disable_write() means "write -+ * the disable register" rather than "disable the ability to write". */ -+#define bm_isr_status_read(bm) __bm_isr_read(bm, bm_isr_status) -+#define bm_isr_status_clear(bm, m) __bm_isr_write(bm, bm_isr_status, m) -+#define bm_isr_enable_read(bm) __bm_isr_read(bm, bm_isr_enable) -+#define bm_isr_enable_write(bm, v) __bm_isr_write(bm, bm_isr_enable, v) -+#define bm_isr_disable_read(bm) __bm_isr_read(bm, bm_isr_disable) -+#define bm_isr_disable_write(bm, v) __bm_isr_write(bm, bm_isr_disable, v) -+#define bm_isr_inhibit(bm) __bm_isr_write(bm, bm_isr_inhibit, 1) -+#define bm_isr_uninhibit(bm) __bm_isr_write(bm, bm_isr_inhibit, 0) -+ -+#ifdef CONFIG_FSL_BMAN_CONFIG -+/* Set depletion thresholds associated with a buffer pool. Requires that the -+ * operating system have access to Bman CCSR (ie. compiled in support and -+ * run-time access courtesy of the device-tree). */ -+int bm_pool_set(u32 bpid, const u32 *thresholds); -+#define BM_POOL_THRESH_SW_ENTER 0 -+#define BM_POOL_THRESH_SW_EXIT 1 -+#define BM_POOL_THRESH_HW_ENTER 2 -+#define BM_POOL_THRESH_HW_EXIT 3 -+ -+/* Read the free buffer count for a given buffer */ -+u32 bm_pool_free_buffers(u32 bpid); -+ -+__init int bman_init(void); -+__init int bman_resource_init(void); -+ -+const struct bm_portal_config *bman_get_bm_portal_config( -+ struct bman_portal *portal); -+ -+/* power management */ -+#ifdef CONFIG_SUSPEND -+void suspend_unused_bportal(void); -+void resume_unused_bportal(void); -+#endif -+ -+#endif /* CONFIG_FSL_BMAN_CONFIG */ ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_test.c -@@ -0,0 +1,56 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "bman_test.h" -+ -+MODULE_AUTHOR("Geoff Thorpe"); -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_DESCRIPTION("Bman testing"); -+ -+static int test_init(void) -+{ -+#ifdef CONFIG_FSL_BMAN_TEST_HIGH -+ int loop = 1; -+ while (loop--) -+ bman_test_high(); -+#endif -+#ifdef CONFIG_FSL_BMAN_TEST_THRESH -+ bman_test_thresh(); -+#endif -+ return 0; -+} -+ -+static void test_exit(void) -+{ -+} -+ -+module_init(test_init); -+module_exit(test_exit); ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_test.h -@@ -0,0 +1,44 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/slab.h> -+#include <linux/module.h> -+#include <linux/interrupt.h> -+#include <linux/delay.h> -+#include <linux/kthread.h> -+ -+#include <linux/fsl_bman.h> -+ -+void bman_test_high(void); -+void bman_test_thresh(void); ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_test_high.c -@@ -0,0 +1,183 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "bman_test.h" -+#include "bman_private.h" -+ -+/*************/ -+/* constants */ -+/*************/ -+ -+#define PORTAL_OPAQUE ((void *)0xf00dbeef) -+#define POOL_OPAQUE ((void *)0xdeadabba) -+#define NUM_BUFS 93 -+#define LOOPS 3 -+#define BMAN_TOKEN_MASK 0x00FFFFFFFFFFLLU -+ -+/***************/ -+/* global vars */ -+/***************/ -+ -+static struct bman_pool *pool; -+static int depleted; -+static struct bm_buffer bufs_in[NUM_BUFS] ____cacheline_aligned; -+static struct bm_buffer bufs_out[NUM_BUFS] ____cacheline_aligned; -+static int bufs_received; -+ -+/* Predeclare the callback so we can instantiate pool parameters */ -+static void depletion_cb(struct bman_portal *, struct bman_pool *, void *, int); -+ -+/**********************/ -+/* internal functions */ -+/**********************/ -+ -+static void bufs_init(void) -+{ -+ int i; -+ for (i = 0; i < NUM_BUFS; i++) -+ bm_buffer_set64(&bufs_in[i], 0xfedc01234567LLU * i); -+ bufs_received = 0; -+} -+ -+static inline int bufs_cmp(const struct bm_buffer *a, const struct bm_buffer *b) -+{ -+ if ((bman_ip_rev == BMAN_REV20) || (bman_ip_rev == BMAN_REV21)) { -+ -+ /* On SoCs with Bman revison 2.0, Bman only respects the 40 -+ * LS-bits of buffer addresses, masking off the upper 8-bits on -+ * release commands. The API provides for 48-bit addresses -+ * because some SoCs support all 48-bits. When generating -+ * garbage addresses for testing, we either need to zero the -+ * upper 8-bits when releasing to Bman (otherwise we'll be -+ * disappointed when the buffers we acquire back from Bman -+ * don't match), or we need to mask the upper 8-bits off when -+ * comparing. We do the latter. -+ */ -+ if ((bm_buffer_get64(a) & BMAN_TOKEN_MASK) -+ < (bm_buffer_get64(b) & BMAN_TOKEN_MASK)) -+ return -1; -+ if ((bm_buffer_get64(a) & BMAN_TOKEN_MASK) -+ > (bm_buffer_get64(b) & BMAN_TOKEN_MASK)) -+ return 1; -+ } else { -+ if (bm_buffer_get64(a) < bm_buffer_get64(b)) -+ return -1; -+ if (bm_buffer_get64(a) > bm_buffer_get64(b)) -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static void bufs_confirm(void) -+{ -+ int i, j; -+ for (i = 0; i < NUM_BUFS; i++) { -+ int matches = 0; -+ for (j = 0; j < NUM_BUFS; j++) -+ if (!bufs_cmp(&bufs_in[i], &bufs_out[j])) -+ matches++; -+ BUG_ON(matches != 1); -+ } -+} -+ -+/********/ -+/* test */ -+/********/ -+ -+static void depletion_cb(struct bman_portal *__portal, struct bman_pool *__pool, -+ void *pool_ctx, int __depleted) -+{ -+ BUG_ON(__pool != pool); -+ BUG_ON(pool_ctx != POOL_OPAQUE); -+ depleted = __depleted; -+} -+ -+void bman_test_high(void) -+{ -+ struct bman_pool_params pparams = { -+ .flags = BMAN_POOL_FLAG_DEPLETION | BMAN_POOL_FLAG_DYNAMIC_BPID, -+ .cb = depletion_cb, -+ .cb_ctx = POOL_OPAQUE, -+ }; -+ int i, loops = LOOPS; -+ struct bm_buffer tmp_buf; -+ -+ bufs_init(); -+ -+ pr_info("BMAN: --- starting high-level test ---\n"); -+ -+ pool = bman_new_pool(&pparams); -+ BUG_ON(!pool); -+ -+ /*******************/ -+ /* Release buffers */ -+ /*******************/ -+do_loop: -+ i = 0; -+ while (i < NUM_BUFS) { -+ u32 flags = BMAN_RELEASE_FLAG_WAIT; -+ int num = 8; -+ if ((i + num) > NUM_BUFS) -+ num = NUM_BUFS - i; -+ if ((i + num) == NUM_BUFS) -+ flags |= BMAN_RELEASE_FLAG_WAIT_SYNC; -+ if (bman_release(pool, bufs_in + i, num, flags)) -+ panic("bman_release() failed\n"); -+ i += num; -+ } -+ -+ /*******************/ -+ /* Acquire buffers */ -+ /*******************/ -+ while (i > 0) { -+ int tmp, num = 8; -+ if (num > i) -+ num = i; -+ tmp = bman_acquire(pool, bufs_out + i - num, num, 0); -+ BUG_ON(tmp != num); -+ i -= num; -+ } -+ -+ i = bman_acquire(pool, &tmp_buf, 1, 0); -+ BUG_ON(i > 0); -+ -+ bufs_confirm(); -+ -+ if (--loops) -+ goto do_loop; -+ -+ /************/ -+ /* Clean up */ -+ /************/ -+ bman_free_pool(pool); -+ pr_info("BMAN: --- finished high-level test ---\n"); -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/bman_test_thresh.c -@@ -0,0 +1,196 @@ -+/* Copyright 2010-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "bman_test.h" -+ -+/* Test constants */ -+#define TEST_NUMBUFS 129728 -+#define TEST_EXIT 129536 -+#define TEST_ENTRY 129024 -+ -+struct affine_test_data { -+ struct task_struct *t; -+ int cpu; -+ int expect_affinity; -+ int drain; -+ int num_enter; -+ int num_exit; -+ struct list_head node; -+ struct completion wakethread; -+ struct completion wakeparent; -+}; -+ -+static void cb_depletion(struct bman_portal *portal, -+ struct bman_pool *pool, -+ void *opaque, -+ int depleted) -+{ -+ struct affine_test_data *data = opaque; -+ int c = smp_processor_id(); -+ pr_info("cb_depletion: bpid=%d, depleted=%d, cpu=%d, original=%d\n", -+ bman_get_params(pool)->bpid, !!depleted, c, data->cpu); -+ /* We should be executing on the CPU of the thread that owns the pool if -+ * and that CPU has an affine portal (ie. it isn't slaved). */ -+ BUG_ON((c != data->cpu) && data->expect_affinity); -+ BUG_ON((c == data->cpu) && !data->expect_affinity); -+ if (depleted) -+ data->num_enter++; -+ else -+ data->num_exit++; -+} -+ -+/* Params used to set up a pool, this also dynamically allocates a BPID */ -+static const struct bman_pool_params params_nocb = { -+ .flags = BMAN_POOL_FLAG_DYNAMIC_BPID | BMAN_POOL_FLAG_THRESH, -+ .thresholds = { TEST_ENTRY, TEST_EXIT, 0, 0 } -+}; -+ -+/* Params used to set up each cpu's pool with callbacks enabled */ -+static struct bman_pool_params params_cb = { -+ .bpid = 0, /* will be replaced to match pool_nocb */ -+ .flags = BMAN_POOL_FLAG_DEPLETION, -+ .cb = cb_depletion -+}; -+ -+static struct bman_pool *pool_nocb; -+static LIST_HEAD(threads); -+ -+static int affine_test(void *__data) -+{ -+ struct bman_pool *pool; -+ struct affine_test_data *data = __data; -+ struct bman_pool_params my_params = params_cb; -+ -+ pr_info("thread %d: starting\n", data->cpu); -+ /* create the pool */ -+ my_params.cb_ctx = data; -+ pool = bman_new_pool(&my_params); -+ BUG_ON(!pool); -+ complete(&data->wakeparent); -+ wait_for_completion(&data->wakethread); -+ init_completion(&data->wakethread); -+ -+ /* if we're the drainer, we get signalled for that */ -+ if (data->drain) { -+ struct bm_buffer buf; -+ int ret; -+ pr_info("thread %d: draining...\n", data->cpu); -+ do { -+ ret = bman_acquire(pool, &buf, 1, 0); -+ } while (ret > 0); -+ pr_info("thread %d: draining done.\n", data->cpu); -+ complete(&data->wakeparent); -+ wait_for_completion(&data->wakethread); -+ init_completion(&data->wakethread); -+ } -+ -+ /* cleanup */ -+ bman_free_pool(pool); -+ while (!kthread_should_stop()) -+ cpu_relax(); -+ pr_info("thread %d: exiting\n", data->cpu); -+ return 0; -+} -+ -+static struct affine_test_data *start_affine_test(int cpu, int drain) -+{ -+ struct affine_test_data *data = kmalloc(sizeof(*data), GFP_KERNEL); -+ -+ if (!data) -+ return NULL; -+ data->cpu = cpu; -+ data->expect_affinity = cpumask_test_cpu(cpu, bman_affine_cpus()); -+ data->drain = drain; -+ data->num_enter = 0; -+ data->num_exit = 0; -+ init_completion(&data->wakethread); -+ init_completion(&data->wakeparent); -+ list_add_tail(&data->node, &threads); -+ data->t = kthread_create(affine_test, data, "threshtest%d", cpu); -+ BUG_ON(IS_ERR(data->t)); -+ kthread_bind(data->t, cpu); -+ wake_up_process(data->t); -+ return data; -+} -+ -+void bman_test_thresh(void) -+{ -+ int loop = TEST_NUMBUFS; -+ int ret, num_cpus = 0; -+ struct affine_test_data *data, *drainer = NULL; -+ -+ pr_info("bman_test_thresh: start\n"); -+ -+ /* allocate a BPID and seed it */ -+ pool_nocb = bman_new_pool(¶ms_nocb); -+ BUG_ON(!pool_nocb); -+ while (loop--) { -+ struct bm_buffer buf; -+ bm_buffer_set64(&buf, 0x0badbeef + loop); -+ ret = bman_release(pool_nocb, &buf, 1, -+ BMAN_RELEASE_FLAG_WAIT); -+ BUG_ON(ret); -+ } -+ while (!bman_rcr_is_empty()) -+ cpu_relax(); -+ pr_info("bman_test_thresh: buffers are in\n"); -+ -+ /* create threads and wait for them to create pools */ -+ params_cb.bpid = bman_get_params(pool_nocb)->bpid; -+ for_each_cpu(loop, cpu_online_mask) { -+ data = start_affine_test(loop, drainer ? 0 : 1); -+ BUG_ON(!data); -+ if (!drainer) -+ drainer = data; -+ num_cpus++; -+ wait_for_completion(&data->wakeparent); -+ } -+ -+ /* signal the drainer to start draining */ -+ complete(&drainer->wakethread); -+ wait_for_completion(&drainer->wakeparent); -+ init_completion(&drainer->wakeparent); -+ -+ /* tear down */ -+ list_for_each_entry_safe(data, drainer, &threads, node) { -+ complete(&data->wakethread); -+ ret = kthread_stop(data->t); -+ BUG_ON(ret); -+ list_del(&data->node); -+ /* check that we get the expected callbacks (and no others) */ -+ BUG_ON(data->num_enter != 1); -+ BUG_ON(data->num_exit != 0); -+ kfree(data); -+ } -+ bman_free_pool(pool_nocb); -+ -+ pr_info("bman_test_thresh: done\n"); -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/dpa_alloc.c -@@ -0,0 +1,706 @@ -+/* Copyright 2009-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "dpa_sys.h" -+#include <linux/fsl_qman.h> -+#include <linux/fsl_bman.h> -+ -+/* Qman and Bman APIs are front-ends to the common code; */ -+ -+static DECLARE_DPA_ALLOC(bpalloc); /* BPID allocator */ -+static DECLARE_DPA_ALLOC(fqalloc); /* FQID allocator */ -+static DECLARE_DPA_ALLOC(qpalloc); /* pool-channel allocator */ -+static DECLARE_DPA_ALLOC(cgralloc); /* CGR ID allocator */ -+static DECLARE_DPA_ALLOC(ceetm0_challoc); /* CEETM Channel ID allocator */ -+static DECLARE_DPA_ALLOC(ceetm0_lfqidalloc); /* CEETM LFQID allocator */ -+static DECLARE_DPA_ALLOC(ceetm1_challoc); /* CEETM Channel ID allocator */ -+static DECLARE_DPA_ALLOC(ceetm1_lfqidalloc); /* CEETM LFQID allocator */ -+ -+/* This is a sort-of-conditional dpa_alloc_free() routine. Eg. when releasing -+ * FQIDs (probably from user-space), it can filter out those that aren't in the -+ * OOS state (better to leak a h/w resource than to crash). This function -+ * returns the number of invalid IDs that were not released. */ -+static u32 release_id_range(struct dpa_alloc *alloc, u32 id, u32 count, -+ int (*is_valid)(u32 id)) -+{ -+ int valid_mode = 0; -+ u32 loop = id, total_invalid = 0; -+ while (loop < (id + count)) { -+ int isvalid = is_valid ? is_valid(loop) : 1; -+ if (!valid_mode) { -+ /* We're looking for a valid ID to terminate an invalid -+ * range */ -+ if (isvalid) { -+ /* We finished a range of invalid IDs, a valid -+ * range is now underway */ -+ valid_mode = 1; -+ count -= (loop - id); -+ id = loop; -+ } else -+ total_invalid++; -+ } else { -+ /* We're looking for an invalid ID to terminate a -+ * valid range */ -+ if (!isvalid) { -+ /* Release the range of valid IDs, an unvalid -+ * range is now underway */ -+ if (loop > id) -+ dpa_alloc_free(alloc, id, loop - id); -+ valid_mode = 0; -+ } -+ } -+ loop++; -+ } -+ /* Release any unterminated range of valid IDs */ -+ if (valid_mode && count) -+ dpa_alloc_free(alloc, id, count); -+ return total_invalid; -+} -+ -+/* BPID allocator front-end */ -+ -+int bman_alloc_bpid_range(u32 *result, u32 count, u32 align, int partial) -+{ -+ return dpa_alloc_new(&bpalloc, result, count, align, partial); -+} -+EXPORT_SYMBOL(bman_alloc_bpid_range); -+ -+static int bp_cleanup(u32 bpid) -+{ -+ return bman_shutdown_pool(bpid) == 0; -+} -+void bman_release_bpid_range(u32 bpid, u32 count) -+{ -+ u32 total_invalid = release_id_range(&bpalloc, bpid, count, bp_cleanup); -+ if (total_invalid) -+ pr_err("BPID range [%d..%d] (%d) had %d leaks\n", -+ bpid, bpid + count - 1, count, total_invalid); -+} -+EXPORT_SYMBOL(bman_release_bpid_range); -+ -+void bman_seed_bpid_range(u32 bpid, u32 count) -+{ -+ dpa_alloc_seed(&bpalloc, bpid, count); -+} -+EXPORT_SYMBOL(bman_seed_bpid_range); -+ -+int bman_reserve_bpid_range(u32 bpid, u32 count) -+{ -+ return dpa_alloc_reserve(&bpalloc, bpid, count); -+} -+EXPORT_SYMBOL(bman_reserve_bpid_range); -+ -+ -+/* FQID allocator front-end */ -+ -+int qman_alloc_fqid_range(u32 *result, u32 count, u32 align, int partial) -+{ -+ return dpa_alloc_new(&fqalloc, result, count, align, partial); -+} -+EXPORT_SYMBOL(qman_alloc_fqid_range); -+ -+static int fq_cleanup(u32 fqid) -+{ -+ return qman_shutdown_fq(fqid) == 0; -+} -+void qman_release_fqid_range(u32 fqid, u32 count) -+{ -+ u32 total_invalid = release_id_range(&fqalloc, fqid, count, fq_cleanup); -+ if (total_invalid) -+ pr_err("FQID range [%d..%d] (%d) had %d leaks\n", -+ fqid, fqid + count - 1, count, total_invalid); -+} -+EXPORT_SYMBOL(qman_release_fqid_range); -+ -+int qman_reserve_fqid_range(u32 fqid, u32 count) -+{ -+ return dpa_alloc_reserve(&fqalloc, fqid, count); -+} -+EXPORT_SYMBOL(qman_reserve_fqid_range); -+ -+void qman_seed_fqid_range(u32 fqid, u32 count) -+{ -+ dpa_alloc_seed(&fqalloc, fqid, count); -+} -+EXPORT_SYMBOL(qman_seed_fqid_range); -+ -+/* Pool-channel allocator front-end */ -+ -+int qman_alloc_pool_range(u32 *result, u32 count, u32 align, int partial) -+{ -+ return dpa_alloc_new(&qpalloc, result, count, align, partial); -+} -+EXPORT_SYMBOL(qman_alloc_pool_range); -+ -+static int qpool_cleanup(u32 qp) -+{ -+ /* We query all FQDs starting from -+ * FQID 1 until we get an "invalid FQID" error, looking for non-OOS FQDs -+ * whose destination channel is the pool-channel being released. -+ * When a non-OOS FQD is found we attempt to clean it up */ -+ struct qman_fq fq = { -+ .fqid = 1 -+ }; -+ int err; -+ do { -+ struct qm_mcr_queryfq_np np; -+ err = qman_query_fq_np(&fq, &np); -+ if (err) -+ /* FQID range exceeded, found no problems */ -+ return 1; -+ if ((np.state & QM_MCR_NP_STATE_MASK) != QM_MCR_NP_STATE_OOS) { -+ struct qm_fqd fqd; -+ err = qman_query_fq(&fq, &fqd); -+ BUG_ON(err); -+ if (fqd.dest.channel == qp) { -+ /* The channel is the FQ's target, clean it */ -+ if (qman_shutdown_fq(fq.fqid) != 0) -+ /* Couldn't shut down the FQ -+ so the pool must be leaked */ -+ return 0; -+ } -+ } -+ /* Move to the next FQID */ -+ fq.fqid++; -+ } while (1); -+} -+void qman_release_pool_range(u32 qp, u32 count) -+{ -+ u32 total_invalid = release_id_range(&qpalloc, qp, -+ count, qpool_cleanup); -+ if (total_invalid) { -+ /* Pool channels are almost always used individually */ -+ if (count == 1) -+ pr_err("Pool channel 0x%x had %d leaks\n", -+ qp, total_invalid); -+ else -+ pr_err("Pool channels [%d..%d] (%d) had %d leaks\n", -+ qp, qp + count - 1, count, total_invalid); -+ } -+} -+EXPORT_SYMBOL(qman_release_pool_range); -+ -+ -+void qman_seed_pool_range(u32 poolid, u32 count) -+{ -+ dpa_alloc_seed(&qpalloc, poolid, count); -+ -+} -+EXPORT_SYMBOL(qman_seed_pool_range); -+ -+int qman_reserve_pool_range(u32 poolid, u32 count) -+{ -+ return dpa_alloc_reserve(&qpalloc, poolid, count); -+} -+EXPORT_SYMBOL(qman_reserve_pool_range); -+ -+ -+/* CGR ID allocator front-end */ -+ -+int qman_alloc_cgrid_range(u32 *result, u32 count, u32 align, int partial) -+{ -+ return dpa_alloc_new(&cgralloc, result, count, align, partial); -+} -+EXPORT_SYMBOL(qman_alloc_cgrid_range); -+ -+static int cqr_cleanup(u32 cgrid) -+{ -+ /* We query all FQDs starting from -+ * FQID 1 until we get an "invalid FQID" error, looking for non-OOS FQDs -+ * whose CGR is the CGR being released. -+ */ -+ struct qman_fq fq = { -+ .fqid = 1 -+ }; -+ int err; -+ do { -+ struct qm_mcr_queryfq_np np; -+ err = qman_query_fq_np(&fq, &np); -+ if (err) -+ /* FQID range exceeded, found no problems */ -+ return 1; -+ if ((np.state & QM_MCR_NP_STATE_MASK) != QM_MCR_NP_STATE_OOS) { -+ struct qm_fqd fqd; -+ err = qman_query_fq(&fq, &fqd); -+ BUG_ON(err); -+ if ((fqd.fq_ctrl & QM_FQCTRL_CGE) && -+ (fqd.cgid == cgrid)) { -+ pr_err("CRGID 0x%x is being used by FQID 0x%x," -+ " CGR will be leaked\n", -+ cgrid, fq.fqid); -+ return 1; -+ } -+ } -+ /* Move to the next FQID */ -+ fq.fqid++; -+ } while (1); -+} -+ -+void qman_release_cgrid_range(u32 cgrid, u32 count) -+{ -+ u32 total_invalid = release_id_range(&cgralloc, cgrid, -+ count, cqr_cleanup); -+ if (total_invalid) -+ pr_err("CGRID range [%d..%d] (%d) had %d leaks\n", -+ cgrid, cgrid + count - 1, count, total_invalid); -+} -+EXPORT_SYMBOL(qman_release_cgrid_range); -+ -+void qman_seed_cgrid_range(u32 cgrid, u32 count) -+{ -+ dpa_alloc_seed(&cgralloc, cgrid, count); -+ -+} -+EXPORT_SYMBOL(qman_seed_cgrid_range); -+ -+/* CEETM CHANNEL ID allocator front-end */ -+int qman_alloc_ceetm0_channel_range(u32 *result, u32 count, u32 align, -+ int partial) -+{ -+ return dpa_alloc_new(&ceetm0_challoc, result, count, align, partial); -+} -+EXPORT_SYMBOL(qman_alloc_ceetm0_channel_range); -+ -+int qman_alloc_ceetm1_channel_range(u32 *result, u32 count, u32 align, -+ int partial) -+{ -+ return dpa_alloc_new(&ceetm1_challoc, result, count, align, partial); -+} -+EXPORT_SYMBOL(qman_alloc_ceetm1_channel_range); -+ -+void qman_release_ceetm0_channel_range(u32 channelid, u32 count) -+{ -+ u32 total_invalid; -+ -+ total_invalid = release_id_range(&ceetm0_challoc, channelid, count, -+ NULL); -+ if (total_invalid) -+ pr_err("CEETM channel range [%d..%d] (%d) had %d leaks\n", -+ channelid, channelid + count - 1, count, total_invalid); -+} -+EXPORT_SYMBOL(qman_release_ceetm0_channel_range); -+ -+void qman_seed_ceetm0_channel_range(u32 channelid, u32 count) -+{ -+ dpa_alloc_seed(&ceetm0_challoc, channelid, count); -+ -+} -+EXPORT_SYMBOL(qman_seed_ceetm0_channel_range); -+ -+void qman_release_ceetm1_channel_range(u32 channelid, u32 count) -+{ -+ u32 total_invalid; -+ total_invalid = release_id_range(&ceetm1_challoc, channelid, count, -+ NULL); -+ if (total_invalid) -+ pr_err("CEETM channel range [%d..%d] (%d) had %d leaks\n", -+ channelid, channelid + count - 1, count, total_invalid); -+} -+EXPORT_SYMBOL(qman_release_ceetm1_channel_range); -+ -+void qman_seed_ceetm1_channel_range(u32 channelid, u32 count) -+{ -+ dpa_alloc_seed(&ceetm1_challoc, channelid, count); -+ -+} -+EXPORT_SYMBOL(qman_seed_ceetm1_channel_range); -+ -+/* CEETM LFQID allocator front-end */ -+int qman_alloc_ceetm0_lfqid_range(u32 *result, u32 count, u32 align, -+ int partial) -+{ -+ return dpa_alloc_new(&ceetm0_lfqidalloc, result, count, align, partial); -+} -+EXPORT_SYMBOL(qman_alloc_ceetm0_lfqid_range); -+ -+int qman_alloc_ceetm1_lfqid_range(u32 *result, u32 count, u32 align, -+ int partial) -+{ -+ return dpa_alloc_new(&ceetm1_lfqidalloc, result, count, align, partial); -+} -+EXPORT_SYMBOL(qman_alloc_ceetm1_lfqid_range); -+ -+void qman_release_ceetm0_lfqid_range(u32 lfqid, u32 count) -+{ -+ u32 total_invalid; -+ -+ total_invalid = release_id_range(&ceetm0_lfqidalloc, lfqid, count, -+ NULL); -+ if (total_invalid) -+ pr_err("CEETM LFQID range [0x%x..0x%x] (%d) had %d leaks\n", -+ lfqid, lfqid + count - 1, count, total_invalid); -+} -+EXPORT_SYMBOL(qman_release_ceetm0_lfqid_range); -+ -+void qman_seed_ceetm0_lfqid_range(u32 lfqid, u32 count) -+{ -+ dpa_alloc_seed(&ceetm0_lfqidalloc, lfqid, count); -+ -+} -+EXPORT_SYMBOL(qman_seed_ceetm0_lfqid_range); -+ -+void qman_release_ceetm1_lfqid_range(u32 lfqid, u32 count) -+{ -+ u32 total_invalid; -+ -+ total_invalid = release_id_range(&ceetm1_lfqidalloc, lfqid, count, -+ NULL); -+ if (total_invalid) -+ pr_err("CEETM LFQID range [0x%x..0x%x] (%d) had %d leaks\n", -+ lfqid, lfqid + count - 1, count, total_invalid); -+} -+EXPORT_SYMBOL(qman_release_ceetm1_lfqid_range); -+ -+void qman_seed_ceetm1_lfqid_range(u32 lfqid, u32 count) -+{ -+ dpa_alloc_seed(&ceetm1_lfqidalloc, lfqid, count); -+ -+} -+EXPORT_SYMBOL(qman_seed_ceetm1_lfqid_range); -+ -+ -+/* Everything else is the common backend to all the allocators */ -+ -+/* The allocator is a (possibly-empty) list of these; */ -+struct alloc_node { -+ struct list_head list; -+ u32 base; -+ u32 num; -+ /* refcount and is_alloced are only set -+ when the node is in the used list */ -+ unsigned int refcount; -+ int is_alloced; -+}; -+ -+/* #define DPA_ALLOC_DEBUG */ -+ -+#ifdef DPA_ALLOC_DEBUG -+#define DPRINT pr_info -+static void DUMP(struct dpa_alloc *alloc) -+{ -+ int off = 0; -+ char buf[256]; -+ struct alloc_node *p; -+ pr_info("Free Nodes\n"); -+ list_for_each_entry(p, &alloc->free, list) { -+ if (off < 255) -+ off += snprintf(buf + off, 255-off, "{%d,%d}", -+ p->base, p->base + p->num - 1); -+ } -+ pr_info("%s\n", buf); -+ -+ off = 0; -+ pr_info("Used Nodes\n"); -+ list_for_each_entry(p, &alloc->used, list) { -+ if (off < 255) -+ off += snprintf(buf + off, 255-off, "{%d,%d}", -+ p->base, p->base + p->num - 1); -+ } -+ pr_info("%s\n", buf); -+ -+ -+ -+} -+#else -+#define DPRINT(x...) -+#define DUMP(a) -+#endif -+ -+int dpa_alloc_new(struct dpa_alloc *alloc, u32 *result, u32 count, u32 align, -+ int partial) -+{ -+ struct alloc_node *i = NULL, *next_best = NULL, *used_node = NULL; -+ u32 base, next_best_base = 0, num = 0, next_best_num = 0; -+ struct alloc_node *margin_left, *margin_right; -+ -+ *result = (u32)-1; -+ DPRINT("alloc_range(%d,%d,%d)\n", count, align, partial); -+ DUMP(alloc); -+ /* If 'align' is 0, it should behave as though it was 1 */ -+ if (!align) -+ align = 1; -+ margin_left = kmalloc(sizeof(*margin_left), GFP_KERNEL); -+ if (!margin_left) -+ goto err; -+ margin_right = kmalloc(sizeof(*margin_right), GFP_KERNEL); -+ if (!margin_right) { -+ kfree(margin_left); -+ goto err; -+ } -+ spin_lock_irq(&alloc->lock); -+ list_for_each_entry(i, &alloc->free, list) { -+ base = (i->base + align - 1) / align; -+ base *= align; -+ if ((base - i->base) >= i->num) -+ /* alignment is impossible, regardless of count */ -+ continue; -+ num = i->num - (base - i->base); -+ if (num >= count) { -+ /* this one will do nicely */ -+ num = count; -+ goto done; -+ } -+ if (num > next_best_num) { -+ next_best = i; -+ next_best_base = base; -+ next_best_num = num; -+ } -+ } -+ if (partial && next_best) { -+ i = next_best; -+ base = next_best_base; -+ num = next_best_num; -+ } else -+ i = NULL; -+done: -+ if (i) { -+ if (base != i->base) { -+ margin_left->base = i->base; -+ margin_left->num = base - i->base; -+ list_add_tail(&margin_left->list, &i->list); -+ } else -+ kfree(margin_left); -+ if ((base + num) < (i->base + i->num)) { -+ margin_right->base = base + num; -+ margin_right->num = (i->base + i->num) - -+ (base + num); -+ list_add(&margin_right->list, &i->list); -+ } else -+ kfree(margin_right); -+ list_del(&i->list); -+ kfree(i); -+ *result = base; -+ } else { -+ spin_unlock_irq(&alloc->lock); -+ kfree(margin_left); -+ kfree(margin_right); -+ } -+ -+err: -+ DPRINT("returning %d\n", i ? num : -ENOMEM); -+ DUMP(alloc); -+ if (!i) -+ return -ENOMEM; -+ -+ /* Add the allocation to the used list with a refcount of 1 */ -+ used_node = kmalloc(sizeof(*used_node), GFP_KERNEL); -+ if (!used_node) { -+ spin_unlock_irq(&alloc->lock); -+ return -ENOMEM; -+ } -+ used_node->base = *result; -+ used_node->num = num; -+ used_node->refcount = 1; -+ used_node->is_alloced = 1; -+ list_add_tail(&used_node->list, &alloc->used); -+ spin_unlock_irq(&alloc->lock); -+ return (int)num; -+} -+ -+/* Allocate the list node using GFP_ATOMIC, because we *really* want to avoid -+ * forcing error-handling on to users in the deallocation path. */ -+static void _dpa_alloc_free(struct dpa_alloc *alloc, u32 base_id, u32 count) -+{ -+ struct alloc_node *i, *node = kmalloc(sizeof(*node), GFP_ATOMIC); -+ BUG_ON(!node); -+ DPRINT("release_range(%d,%d)\n", base_id, count); -+ DUMP(alloc); -+ BUG_ON(!count); -+ spin_lock_irq(&alloc->lock); -+ -+ -+ node->base = base_id; -+ node->num = count; -+ list_for_each_entry(i, &alloc->free, list) { -+ if (i->base >= node->base) { -+ /* BUG_ON(any overlapping) */ -+ BUG_ON(i->base < (node->base + node->num)); -+ list_add_tail(&node->list, &i->list); -+ goto done; -+ } -+ } -+ list_add_tail(&node->list, &alloc->free); -+done: -+ /* Merge to the left */ -+ i = list_entry(node->list.prev, struct alloc_node, list); -+ if (node->list.prev != &alloc->free) { -+ BUG_ON((i->base + i->num) > node->base); -+ if ((i->base + i->num) == node->base) { -+ node->base = i->base; -+ node->num += i->num; -+ list_del(&i->list); -+ kfree(i); -+ } -+ } -+ /* Merge to the right */ -+ i = list_entry(node->list.next, struct alloc_node, list); -+ if (node->list.next != &alloc->free) { -+ BUG_ON((node->base + node->num) > i->base); -+ if ((node->base + node->num) == i->base) { -+ node->num += i->num; -+ list_del(&i->list); -+ kfree(i); -+ } -+ } -+ spin_unlock_irq(&alloc->lock); -+ DUMP(alloc); -+} -+ -+ -+void dpa_alloc_free(struct dpa_alloc *alloc, u32 base_id, u32 count) -+{ -+ struct alloc_node *i = NULL; -+ spin_lock_irq(&alloc->lock); -+ -+ /* First find the node in the used list and decrement its ref count */ -+ list_for_each_entry(i, &alloc->used, list) { -+ if (i->base == base_id && i->num == count) { -+ --i->refcount; -+ if (i->refcount == 0) { -+ list_del(&i->list); -+ spin_unlock_irq(&alloc->lock); -+ if (i->is_alloced) -+ _dpa_alloc_free(alloc, base_id, count); -+ kfree(i); -+ return; -+ } -+ spin_unlock_irq(&alloc->lock); -+ return; -+ } -+ } -+ /* Couldn't find the allocation */ -+ pr_err("Attempt to free ID 0x%x COUNT %d that wasn't alloc'd or reserved\n", -+ base_id, count); -+ spin_unlock_irq(&alloc->lock); -+} -+ -+void dpa_alloc_seed(struct dpa_alloc *alloc, u32 base_id, u32 count) -+{ -+ /* Same as free but no previous allocation checking is needed */ -+ _dpa_alloc_free(alloc, base_id, count); -+} -+ -+ -+int dpa_alloc_reserve(struct dpa_alloc *alloc, u32 base, u32 num) -+{ -+ struct alloc_node *i = NULL, *used_node; -+ -+ DPRINT("alloc_reserve(%d,%d)\n", base, num); -+ DUMP(alloc); -+ -+ spin_lock_irq(&alloc->lock); -+ -+ /* Check for the node in the used list. -+ If found, increase it's refcount */ -+ list_for_each_entry(i, &alloc->used, list) { -+ if ((i->base == base) && (i->num == num)) { -+ ++i->refcount; -+ spin_unlock_irq(&alloc->lock); -+ return 0; -+ } -+ if ((base >= i->base) && (base < (i->base + i->num))) { -+ /* This is an attempt to reserve a region that was -+ already reserved or alloced with a different -+ base or num */ -+ pr_err("Cannot reserve %d - %d, it overlaps with" -+ " existing reservation from %d - %d\n", -+ base, base + num - 1, i->base, -+ i->base + i->num - 1); -+ spin_unlock_irq(&alloc->lock); -+ return -1; -+ } -+ } -+ /* Check to make sure this ID isn't in the free list */ -+ list_for_each_entry(i, &alloc->free, list) { -+ if ((base >= i->base) && (base < (i->base + i->num))) { -+ /* yep, the reservation is within this node */ -+ pr_err("Cannot reserve %d - %d, it overlaps with" -+ " free range %d - %d and must be alloced\n", -+ base, base + num - 1, -+ i->base, i->base + i->num - 1); -+ spin_unlock_irq(&alloc->lock); -+ return -1; -+ } -+ } -+ /* Add the allocation to the used list with a refcount of 1 */ -+ used_node = kmalloc(sizeof(*used_node), GFP_KERNEL); -+ if (!used_node) { -+ spin_unlock_irq(&alloc->lock); -+ return -ENOMEM; -+ -+ } -+ used_node->base = base; -+ used_node->num = num; -+ used_node->refcount = 1; -+ used_node->is_alloced = 0; -+ list_add_tail(&used_node->list, &alloc->used); -+ spin_unlock_irq(&alloc->lock); -+ return 0; -+} -+ -+ -+int dpa_alloc_pop(struct dpa_alloc *alloc, u32 *result, u32 *count) -+{ -+ struct alloc_node *i = NULL; -+ DPRINT("alloc_pop()\n"); -+ DUMP(alloc); -+ spin_lock_irq(&alloc->lock); -+ if (!list_empty(&alloc->free)) { -+ i = list_entry(alloc->free.next, struct alloc_node, list); -+ list_del(&i->list); -+ } -+ spin_unlock_irq(&alloc->lock); -+ DPRINT("returning %d\n", i ? 0 : -ENOMEM); -+ DUMP(alloc); -+ if (!i) -+ return -ENOMEM; -+ *result = i->base; -+ *count = i->num; -+ kfree(i); -+ return 0; -+} -+ -+int dpa_alloc_check(struct dpa_alloc *list_head, u32 item) -+{ -+ struct alloc_node *i = NULL; -+ int res = 0; -+ DPRINT("alloc_check()\n"); -+ spin_lock_irq(&list_head->lock); -+ -+ list_for_each_entry(i, &list_head->free, list) { -+ if ((item >= i->base) && (item < (i->base + i->num))) { -+ res = 1; -+ break; -+ } -+ } -+ spin_unlock_irq(&list_head->lock); -+ return res; -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/dpa_sys.h -@@ -0,0 +1,259 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef DPA_SYS_H -+#define DPA_SYS_H -+ -+#include <linux/kernel.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/dma-mapping.h> -+#include <linux/bootmem.h> -+#include <linux/slab.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/interrupt.h> -+#include <linux/delay.h> -+#include <linux/of_platform.h> -+#include <linux/of_address.h> -+#include <linux/of_irq.h> -+#include <linux/kthread.h> -+#include <linux/memblock.h> -+#include <linux/completion.h> -+#include <linux/log2.h> -+#include <linux/types.h> -+#include <linux/ioctl.h> -+#include <linux/miscdevice.h> -+#include <linux/uaccess.h> -+#include <linux/debugfs.h> -+#include <linux/seq_file.h> -+#include <linux/device.h> -+#include <linux/uio_driver.h> -+#include <linux/smp.h> -+#include <linux/fsl_hypervisor.h> -+#include <linux/vmalloc.h> -+#include <linux/ctype.h> -+#include <linux/math64.h> -+#include <linux/bitops.h> -+ -+#include <linux/fsl_usdpaa.h> -+ -+/* When copying aligned words or shorts, try to avoid memcpy() */ -+#define CONFIG_TRY_BETTER_MEMCPY -+ -+/* For 2-element tables related to cache-inhibited and cache-enabled mappings */ -+#define DPA_PORTAL_CE 0 -+#define DPA_PORTAL_CI 1 -+ -+/***********************/ -+/* Misc inline assists */ -+/***********************/ -+ -+#if defined CONFIG_PPC32 -+#include "dpa_sys_ppc32.h" -+#elif defined CONFIG_PPC64 -+#include "dpa_sys_ppc64.h" -+#elif defined CONFIG_ARM -+#include "dpa_sys_arm.h" -+#elif defined CONFIG_ARM64 -+#include "dpa_sys_arm64.h" -+#endif -+ -+ -+#ifdef CONFIG_FSL_DPA_CHECKING -+#define DPA_ASSERT(x) \ -+ do { \ -+ if (!(x)) { \ -+ pr_crit("ASSERT: (%s:%d) %s\n", __FILE__, __LINE__, \ -+ __stringify_1(x)); \ -+ dump_stack(); \ -+ panic("assertion failure"); \ -+ } \ -+ } while (0) -+#else -+#define DPA_ASSERT(x) -+#endif -+ -+/* memcpy() stuff - when you know alignments in advance */ -+#ifdef CONFIG_TRY_BETTER_MEMCPY -+static inline void copy_words(void *dest, const void *src, size_t sz) -+{ -+ u32 *__dest = dest; -+ const u32 *__src = src; -+ size_t __sz = sz >> 2; -+ BUG_ON((unsigned long)dest & 0x3); -+ BUG_ON((unsigned long)src & 0x3); -+ BUG_ON(sz & 0x3); -+ while (__sz--) -+ *(__dest++) = *(__src++); -+} -+static inline void copy_shorts(void *dest, const void *src, size_t sz) -+{ -+ u16 *__dest = dest; -+ const u16 *__src = src; -+ size_t __sz = sz >> 1; -+ BUG_ON((unsigned long)dest & 0x1); -+ BUG_ON((unsigned long)src & 0x1); -+ BUG_ON(sz & 0x1); -+ while (__sz--) -+ *(__dest++) = *(__src++); -+} -+static inline void copy_bytes(void *dest, const void *src, size_t sz) -+{ -+ u8 *__dest = dest; -+ const u8 *__src = src; -+ while (sz--) -+ *(__dest++) = *(__src++); -+} -+#else -+#define copy_words memcpy -+#define copy_shorts memcpy -+#define copy_bytes memcpy -+#endif -+ -+/************/ -+/* RB-trees */ -+/************/ -+ -+/* We encapsulate RB-trees so that its easier to use non-linux forms in -+ * non-linux systems. This also encapsulates the extra plumbing that linux code -+ * usually provides when using RB-trees. This encapsulation assumes that the -+ * data type held by the tree is u32. */ -+ -+struct dpa_rbtree { -+ struct rb_root root; -+}; -+#define DPA_RBTREE { .root = RB_ROOT } -+ -+static inline void dpa_rbtree_init(struct dpa_rbtree *tree) -+{ -+ tree->root = RB_ROOT; -+} -+ -+#define IMPLEMENT_DPA_RBTREE(name, type, node_field, val_field) \ -+static inline int name##_push(struct dpa_rbtree *tree, type *obj) \ -+{ \ -+ struct rb_node *parent = NULL, **p = &tree->root.rb_node; \ -+ while (*p) { \ -+ u32 item; \ -+ parent = *p; \ -+ item = rb_entry(parent, type, node_field)->val_field; \ -+ if (obj->val_field < item) \ -+ p = &parent->rb_left; \ -+ else if (obj->val_field > item) \ -+ p = &parent->rb_right; \ -+ else \ -+ return -EBUSY; \ -+ } \ -+ rb_link_node(&obj->node_field, parent, p); \ -+ rb_insert_color(&obj->node_field, &tree->root); \ -+ return 0; \ -+} \ -+static inline void name##_del(struct dpa_rbtree *tree, type *obj) \ -+{ \ -+ rb_erase(&obj->node_field, &tree->root); \ -+} \ -+static inline type *name##_find(struct dpa_rbtree *tree, u32 val) \ -+{ \ -+ type *ret; \ -+ struct rb_node *p = tree->root.rb_node; \ -+ while (p) { \ -+ ret = rb_entry(p, type, node_field); \ -+ if (val < ret->val_field) \ -+ p = p->rb_left; \ -+ else if (val > ret->val_field) \ -+ p = p->rb_right; \ -+ else \ -+ return ret; \ -+ } \ -+ return NULL; \ -+} -+ -+/************/ -+/* Bootargs */ -+/************/ -+ -+/* Qman has "qportals=" and Bman has "bportals=", they use the same syntax -+ * though; a comma-separated list of items, each item being a cpu index and/or a -+ * range of cpu indices, and each item optionally be prefixed by "s" to indicate -+ * that the portal associated with that cpu should be shared. See bman_driver.c -+ * for more specifics. */ -+static int __parse_portals_cpu(const char **s, unsigned int *cpu) -+{ -+ *cpu = 0; -+ if (!isdigit(**s)) -+ return -EINVAL; -+ while (isdigit(**s)) -+ *cpu = *cpu * 10 + (*((*s)++) - '0'); -+ return 0; -+} -+static inline int parse_portals_bootarg(char *str, struct cpumask *want_shared, -+ struct cpumask *want_unshared, -+ const char *argname) -+{ -+ const char *s = str; -+ unsigned int shared, cpu1, cpu2, loop; -+ -+keep_going: -+ if (*s == 's') { -+ shared = 1; -+ s++; -+ } else -+ shared = 0; -+ if (__parse_portals_cpu(&s, &cpu1)) -+ goto err; -+ if (*s == '-') { -+ s++; -+ if (__parse_portals_cpu(&s, &cpu2)) -+ goto err; -+ if (cpu2 < cpu1) -+ goto err; -+ } else -+ cpu2 = cpu1; -+ for (loop = cpu1; loop <= cpu2; loop++) -+ cpumask_set_cpu(loop, shared ? want_shared : want_unshared); -+ if (*s == ',') { -+ s++; -+ goto keep_going; -+ } else if ((*s == '\0') || isspace(*s)) -+ return 0; -+err: -+ pr_crit("Malformed %s argument: %s, offset: %lu\n", argname, str, -+ (unsigned long)s - (unsigned long)str); -+ return -EINVAL; -+} -+#ifdef CONFIG_FSL_USDPAA -+/* Hooks from fsl_usdpaa_irq.c to fsl_usdpaa.c */ -+int usdpaa_get_portal_config(struct file *filp, void *cinh, -+ enum usdpaa_portal_type ptype, unsigned int *irq, -+ void **iir_reg); -+#endif -+#endif /* DPA_SYS_H */ ---- /dev/null -+++ b/drivers/staging/fsl_qbman/dpa_sys_arm.h -@@ -0,0 +1,95 @@ -+/* Copyright 2016 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef DPA_SYS_ARM_H -+#define DPA_SYS_ARM_H -+ -+#include <asm/cacheflush.h> -+#include <asm/barrier.h> -+ -+/* Implementation of ARM specific routines */ -+ -+/* TODO: NB, we currently assume that hwsync() and lwsync() imply compiler -+ * barriers and that dcb*() won't fall victim to compiler or execution -+ * reordering with respect to other code/instructions that manipulate the same -+ * cacheline. */ -+#define hwsync() { asm volatile("dmb st" : : : "memory"); } -+#define lwsync() { asm volatile("dmb st" : : : "memory"); } -+#define dcbf(p) { asm volatile("mcr p15, 0, %0, c7, c10, 1" : : "r" (p) : "memory"); } -+#define dcbt_ro(p) { asm volatile("pld [%0, #64];": : "r" (p)); } -+#define dcbt_rw(p) { asm volatile("pldw [%0, #64];": : "r" (p)); } -+#define dcbi(p) { asm volatile("mcr p15, 0, %0, c7, c6, 1" : : "r" (p) : "memory"); } -+ -+#define dcbz_64(p) { memset(p, 0, sizeof(*p)); } -+ -+#define dcbf_64(p) \ -+ do { \ -+ dcbf((u32)p); \ -+ } while (0) -+/* Commonly used combo */ -+#define dcbit_ro(p) \ -+ do { \ -+ dcbi((u32)p); \ -+ dcbt_ro((u32)p); \ -+ } while (0) -+ -+static inline u64 mfatb(void) -+{ -+ return get_cycles(); -+} -+ -+static inline u32 in_be32(volatile void *addr) -+{ -+ return be32_to_cpu(*((volatile u32 *) addr)); -+} -+ -+static inline void out_be32(void *addr, u32 val) -+{ -+ *((u32 *) addr) = cpu_to_be32(val); -+} -+ -+ -+static inline void set_bits(unsigned long mask, volatile unsigned long *p) -+{ -+ *p |= mask; -+} -+static inline void clear_bits(unsigned long mask, volatile unsigned long *p) -+{ -+ *p &= ~mask; -+} -+ -+static inline void flush_dcache_range(unsigned long start, unsigned long stop) -+{ -+ __cpuc_flush_dcache_area((void *) start, stop - start); -+} -+ -+#define hard_smp_processor_id() raw_smp_processor_id() -+#endif ---- /dev/null -+++ b/drivers/staging/fsl_qbman/dpa_sys_arm64.h -@@ -0,0 +1,102 @@ -+/* Copyright 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef DPA_SYS_ARM64_H -+#define DPA_SYS_ARM64_H -+ -+#include <asm/cacheflush.h> -+#include <asm/barrier.h> -+ -+/* Implementation of ARM 64 bit specific routines */ -+ -+/* TODO: NB, we currently assume that hwsync() and lwsync() imply compiler -+ * barriers and that dcb*() won't fall victim to compiler or execution -+ * reordering with respect to other code/instructions that manipulate the same -+ * cacheline. */ -+#define hwsync() { asm volatile("dmb st" : : : "memory"); } -+#define lwsync() { asm volatile("dmb st" : : : "memory"); } -+#define dcbf(p) { asm volatile("dc cvac, %0;" : : "r" (p) : "memory"); } -+#define dcbt_ro(p) { asm volatile("prfm pldl1keep, [%0, #0]" : : "r" (p)); } -+#define dcbt_rw(p) { asm volatile("prfm pstl1keep, [%0, #0]" : : "r" (p)); } -+#define dcbi(p) { asm volatile("dc ivac, %0" : : "r"(p) : "memory"); } -+#define dcbz(p) { asm volatile("dc zva, %0" : : "r" (p) : "memory"); } -+ -+#define dcbz_64(p) \ -+ do { \ -+ dcbz(p); \ -+ } while (0) -+ -+#define dcbf_64(p) \ -+ do { \ -+ dcbf(p); \ -+ } while (0) -+/* Commonly used combo */ -+#define dcbit_ro(p) \ -+ do { \ -+ dcbi(p); \ -+ dcbt_ro(p); \ -+ } while (0) -+ -+static inline u64 mfatb(void) -+{ -+ return get_cycles(); -+} -+ -+static inline u32 in_be32(volatile void *addr) -+{ -+ return be32_to_cpu(*((volatile u32 *) addr)); -+} -+ -+static inline void out_be32(void *addr, u32 val) -+{ -+ *((u32 *) addr) = cpu_to_be32(val); -+} -+ -+ -+static inline void set_bits(unsigned long mask, volatile unsigned long *p) -+{ -+ *p |= mask; -+} -+static inline void clear_bits(unsigned long mask, volatile unsigned long *p) -+{ -+ *p &= ~mask; -+} -+ -+static inline void flush_dcache_range(unsigned long start, unsigned long stop) -+{ -+ __flush_dcache_area((void *) start, stop - start); -+} -+ -+#define hard_smp_processor_id() raw_smp_processor_id() -+ -+ -+ -+#endif ---- /dev/null -+++ b/drivers/staging/fsl_qbman/dpa_sys_ppc32.h -@@ -0,0 +1,70 @@ -+/* Copyright 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef DPA_SYS_PPC32_H -+#define DPA_SYS_PPC32_H -+ -+/* Implementation of PowerPC 32 bit specific routines */ -+ -+/* TODO: NB, we currently assume that hwsync() and lwsync() imply compiler -+ * barriers and that dcb*() won't fall victim to compiler or execution -+ * reordering with respect to other code/instructions that manipulate the same -+ * cacheline. */ -+#define hwsync() __asm__ __volatile__ ("sync" : : : "memory") -+#define lwsync() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : : "memory") -+#define dcbf(p) __asm__ __volatile__ ("dcbf 0,%0" : : "r" (p) : "memory") -+#define dcbt_ro(p) __asm__ __volatile__ ("dcbt 0,%0" : : "r" (p)) -+#define dcbt_rw(p) __asm__ __volatile__ ("dcbtst 0,%0" : : "r" (p)) -+#define dcbi(p) dcbf(p) -+ -+#define dcbzl(p) __asm__ __volatile__ ("dcbzl 0,%0" : : "r" (p)) -+#define dcbz_64(p) dcbzl(p) -+#define dcbf_64(p) dcbf(p) -+ -+/* Commonly used combo */ -+#define dcbit_ro(p) \ -+ do { \ -+ dcbi(p); \ -+ dcbt_ro(p); \ -+ } while (0) -+ -+static inline u64 mfatb(void) -+{ -+ u32 hi, lo, chk; -+ do { -+ hi = mfspr(SPRN_ATBU); -+ lo = mfspr(SPRN_ATBL); -+ chk = mfspr(SPRN_ATBU); -+ } while (unlikely(hi != chk)); -+ return ((u64)hi << 32) | (u64)lo; -+} -+ -+#endif ---- /dev/null -+++ b/drivers/staging/fsl_qbman/dpa_sys_ppc64.h -@@ -0,0 +1,79 @@ -+/* Copyright 2014 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef DPA_SYS_PPC64_H -+#define DPA_SYS_PPC64_H -+ -+/* Implementation of PowerPC 64 bit specific routines */ -+ -+/* TODO: NB, we currently assume that hwsync() and lwsync() imply compiler -+ * barriers and that dcb*() won't fall victim to compiler or execution -+ * reordering with respect to other code/instructions that manipulate the same -+ * cacheline. */ -+#define hwsync() __asm__ __volatile__ ("sync" : : : "memory") -+#define lwsync() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : : "memory") -+#define dcbf(p) __asm__ __volatile__ ("dcbf 0,%0" : : "r" (p) : "memory") -+#define dcbt_ro(p) __asm__ __volatile__ ("dcbt 0,%0" : : "r" (p)) -+#define dcbt_rw(p) __asm__ __volatile__ ("dcbtst 0,%0" : : "r" (p)) -+#define dcbi(p) dcbf(p) -+ -+#define dcbz(p) __asm__ __volatile__ ("dcbz 0,%0" : : "r" (p)) -+#define dcbz_64(p) \ -+ do { \ -+ dcbz((void*)p + 32); \ -+ dcbz(p); \ -+ } while (0) -+#define dcbf_64(p) \ -+ do { \ -+ dcbf((void*)p + 32); \ -+ dcbf(p); \ -+ } while (0) -+/* Commonly used combo */ -+#define dcbit_ro(p) \ -+ do { \ -+ dcbi(p); \ -+ dcbi((void*)p + 32); \ -+ dcbt_ro(p); \ -+ dcbt_ro((void*)p + 32); \ -+ } while (0) -+ -+static inline u64 mfatb(void) -+{ -+ u32 hi, lo, chk; -+ do { -+ hi = mfspr(SPRN_ATBU); -+ lo = mfspr(SPRN_ATBL); -+ chk = mfspr(SPRN_ATBU); -+ } while (unlikely(hi != chk)); -+ return ((u64)hi << 32) | (u64)lo; -+} -+ -+#endif ---- /dev/null -+++ b/drivers/staging/fsl_qbman/fsl_usdpaa.c -@@ -0,0 +1,1984 @@ -+/* Copyright (C) 2008-2012 Freescale Semiconductor, Inc. -+ * Authors: Andy Fleming <afleming@freescale.com> -+ * Timur Tabi <timur@freescale.com> -+ * Geoff Thorpe <Geoff.Thorpe@freescale.com> -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+ -+#include <linux/miscdevice.h> -+#include <linux/fs.h> -+#include <linux/cdev.h> -+#include <linux/mm.h> -+#include <linux/of.h> -+#include <linux/memblock.h> -+#include <linux/slab.h> -+#include <linux/mman.h> -+#include <linux/of_reserved_mem.h> -+ -+#if !(defined(CONFIG_ARM) || defined(CONFIG_ARM64)) -+#include <mm/mmu_decl.h> -+#endif -+ -+#include "dpa_sys.h" -+#include <linux/fsl_usdpaa.h> -+#include "bman_low.h" -+#include "qman_low.h" -+ -+/* Physical address range of the memory reservation, exported for mm/mem.c */ -+static u64 phys_start; -+static u64 phys_size; -+static u64 arg_phys_size; -+ -+/* PFN versions of the above */ -+static unsigned long pfn_start; -+static unsigned long pfn_size; -+ -+/* Memory reservations are manipulated under this spinlock (which is why 'refs' -+ * isn't atomic_t). */ -+static DEFINE_SPINLOCK(mem_lock); -+ -+/* The range of TLB1 indices */ -+static unsigned int first_tlb; -+static unsigned int num_tlb = 1; -+static unsigned int current_tlb; /* loops around for fault handling */ -+ -+/* Memory reservation is represented as a list of 'mem_fragment's, some of which -+ * may be mapped. Unmapped fragments are always merged where possible. */ -+static LIST_HEAD(mem_list); -+ -+struct mem_mapping; -+ -+/* Memory fragments are in 'mem_list'. */ -+struct mem_fragment { -+ u64 base; -+ u64 len; -+ unsigned long pfn_base; /* PFN version of 'base' */ -+ unsigned long pfn_len; /* PFN version of 'len' */ -+ unsigned int refs; /* zero if unmapped */ -+ u64 root_len; /* Size of the orignal fragment */ -+ unsigned long root_pfn; /* PFN of the orignal fragment */ -+ struct list_head list; -+ /* if mapped, flags+name captured at creation time */ -+ u32 flags; -+ char name[USDPAA_DMA_NAME_MAX]; -+ u64 map_len; -+ /* support multi-process locks per-memory-fragment. */ -+ int has_locking; -+ wait_queue_head_t wq; -+ struct mem_mapping *owner; -+}; -+ -+/* Mappings of memory fragments in 'struct ctx'. These are created from -+ * ioctl(USDPAA_IOCTL_DMA_MAP), though the actual mapping then happens via a -+ * mmap(). */ -+struct mem_mapping { -+ struct mem_fragment *root_frag; -+ u32 frag_count; -+ u64 total_size; -+ struct list_head list; -+ int refs; -+ void *virt_addr; -+}; -+ -+struct portal_mapping { -+ struct usdpaa_ioctl_portal_map user; -+ union { -+ struct qm_portal_config *qportal; -+ struct bm_portal_config *bportal; -+ }; -+ /* Declare space for the portals in case the process -+ exits unexpectedly and needs to be cleaned by the kernel */ -+ union { -+ struct qm_portal qman_portal_low; -+ struct bm_portal bman_portal_low; -+ }; -+ struct list_head list; -+ struct resource *phys; -+ struct iommu_domain *iommu_domain; -+}; -+ -+/* Track the DPAA resources the process is using */ -+struct active_resource { -+ struct list_head list; -+ u32 id; -+ u32 num; -+ unsigned int refcount; -+}; -+ -+/* Per-FD state (which should also be per-process but we don't enforce that) */ -+struct ctx { -+ /* Lock to protect the context */ -+ spinlock_t lock; -+ /* Allocated resources get put here for accounting */ -+ struct list_head resources[usdpaa_id_max]; -+ /* list of DMA maps */ -+ struct list_head maps; -+ /* list of portal maps */ -+ struct list_head portals; -+}; -+ -+/* Different resource classes */ -+static const struct alloc_backend { -+ enum usdpaa_id_type id_type; -+ int (*alloc)(u32 *, u32, u32, int); -+ void (*release)(u32 base, unsigned int count); -+ int (*reserve)(u32 base, unsigned int count); -+ const char *acronym; -+} alloc_backends[] = { -+ { -+ .id_type = usdpaa_id_fqid, -+ .alloc = qman_alloc_fqid_range, -+ .release = qman_release_fqid_range, -+ .reserve = qman_reserve_fqid_range, -+ .acronym = "FQID" -+ }, -+ { -+ .id_type = usdpaa_id_bpid, -+ .alloc = bman_alloc_bpid_range, -+ .release = bman_release_bpid_range, -+ .reserve = bman_reserve_bpid_range, -+ .acronym = "BPID" -+ }, -+ { -+ .id_type = usdpaa_id_qpool, -+ .alloc = qman_alloc_pool_range, -+ .release = qman_release_pool_range, -+ .reserve = qman_reserve_pool_range, -+ .acronym = "QPOOL" -+ }, -+ { -+ .id_type = usdpaa_id_cgrid, -+ .alloc = qman_alloc_cgrid_range, -+ .release = qman_release_cgrid_range, -+ .acronym = "CGRID" -+ }, -+ { -+ .id_type = usdpaa_id_ceetm0_lfqid, -+ .alloc = qman_alloc_ceetm0_lfqid_range, -+ .release = qman_release_ceetm0_lfqid_range, -+ .acronym = "CEETM0_LFQID" -+ }, -+ { -+ .id_type = usdpaa_id_ceetm0_channelid, -+ .alloc = qman_alloc_ceetm0_channel_range, -+ .release = qman_release_ceetm0_channel_range, -+ .acronym = "CEETM0_LFQID" -+ }, -+ { -+ .id_type = usdpaa_id_ceetm1_lfqid, -+ .alloc = qman_alloc_ceetm1_lfqid_range, -+ .release = qman_release_ceetm1_lfqid_range, -+ .acronym = "CEETM1_LFQID" -+ }, -+ { -+ .id_type = usdpaa_id_ceetm1_channelid, -+ .alloc = qman_alloc_ceetm1_channel_range, -+ .release = qman_release_ceetm1_channel_range, -+ .acronym = "CEETM1_LFQID" -+ }, -+ { -+ /* This terminates the array */ -+ .id_type = usdpaa_id_max -+ } -+}; -+ -+/* Determines the largest acceptable page size for a given size -+ The sizes are determined by what the TLB1 acceptable page sizes are */ -+static u32 largest_page_size(u32 size) -+{ -+ int shift = 30; /* Start at 1G size */ -+ if (size < 4096) -+ return 0; -+ do { -+ if (size >= (1<<shift)) -+ return 1<<shift; -+ shift -= 2; -+ } while (shift >= 12); /* Up to 4k */ -+ return 0; -+} -+ -+/* Determine if value is power of 4 */ -+static inline bool is_power_of_4(u64 x) -+{ -+ if (x == 0 || ((x & (x - 1)) != 0)) -+ return false; -+ return !!(x & 0x5555555555555555ull); -+} -+ -+/* Helper for ioctl_dma_map() when we have a larger fragment than we need. This -+ * splits the fragment into 4 and returns the upper-most. (The caller can loop -+ * until it has a suitable fragment size.) */ -+static struct mem_fragment *split_frag(struct mem_fragment *frag) -+{ -+ struct mem_fragment *x[3]; -+ -+ x[0] = kmalloc(sizeof(struct mem_fragment), GFP_ATOMIC); -+ x[1] = kmalloc(sizeof(struct mem_fragment), GFP_ATOMIC); -+ x[2] = kmalloc(sizeof(struct mem_fragment), GFP_ATOMIC); -+ if (!x[0] || !x[1] || !x[2]) { -+ kfree(x[0]); -+ kfree(x[1]); -+ kfree(x[2]); -+ return NULL; -+ } -+ BUG_ON(frag->refs); -+ frag->len >>= 2; -+ frag->pfn_len >>= 2; -+ x[0]->base = frag->base + frag->len; -+ x[1]->base = x[0]->base + frag->len; -+ x[2]->base = x[1]->base + frag->len; -+ x[0]->len = x[1]->len = x[2]->len = frag->len; -+ x[0]->pfn_base = frag->pfn_base + frag->pfn_len; -+ x[1]->pfn_base = x[0]->pfn_base + frag->pfn_len; -+ x[2]->pfn_base = x[1]->pfn_base + frag->pfn_len; -+ x[0]->pfn_len = x[1]->pfn_len = x[2]->pfn_len = frag->pfn_len; -+ x[0]->refs = x[1]->refs = x[2]->refs = 0; -+ x[0]->root_len = x[1]->root_len = x[2]->root_len = frag->root_len; -+ x[0]->root_pfn = x[1]->root_pfn = x[2]->root_pfn = frag->root_pfn; -+ x[0]->name[0] = x[1]->name[0] = x[2]->name[0] = 0; -+ list_add_tail(&x[0]->list, &frag->list); -+ list_add_tail(&x[1]->list, &x[0]->list); -+ list_add_tail(&x[2]->list, &x[1]->list); -+ return x[2]; -+} -+ -+static __maybe_unused void dump_frags(void) -+{ -+ struct mem_fragment *frag; -+ int i = 0; -+ list_for_each_entry(frag, &mem_list, list) { -+ pr_info("FRAG %d: base 0x%llx pfn_base 0x%lx len 0x%llx root_len 0x%llx root_pfn 0x%lx refs %d name %s\n", -+ i, frag->base, frag->pfn_base, -+ frag->len, frag->root_len, frag->root_pfn, -+ frag->refs, frag->name); -+ ++i; -+ } -+} -+ -+/* Walk the list of fragments and adjoin neighbouring segments if possible */ -+static void compress_frags(void) -+{ -+ /* Walk the fragment list and combine fragments */ -+ struct mem_fragment *frag, *nxtfrag; -+ u64 len = 0; -+ -+ int i, numfrags; -+ -+ -+ frag = list_entry(mem_list.next, struct mem_fragment, list); -+ -+ while (&frag->list != &mem_list) { -+ /* Must combine consecutive fragemenst with -+ same root_pfn such that they are power of 4 */ -+ if (frag->refs != 0) { -+ frag = list_entry(frag->list.next, -+ struct mem_fragment, list); -+ continue; /* Not this window */ -+ } -+ len = frag->len; -+ numfrags = 0; -+ nxtfrag = list_entry(frag->list.next, -+ struct mem_fragment, list); -+ while (true) { -+ if (&nxtfrag->list == &mem_list) { -+ numfrags = 0; -+ break; /* End of list */ -+ } -+ if (nxtfrag->refs) { -+ numfrags = 0; -+ break; /* In use still */ -+ } -+ if (nxtfrag->root_pfn != frag->root_pfn) { -+ numfrags = 0; -+ break; /* Crosses root fragment boundary */ -+ } -+ len += nxtfrag->len; -+ numfrags++; -+ if (is_power_of_4(len)) { -+ /* These fragments can be combined */ -+ break; -+ } -+ nxtfrag = list_entry(nxtfrag->list.next, -+ struct mem_fragment, list); -+ } -+ if (numfrags == 0) { -+ frag = list_entry(frag->list.next, -+ struct mem_fragment, list); -+ continue; /* try the next window */ -+ } -+ for (i = 0; i < numfrags; i++) { -+ struct mem_fragment *todel = -+ list_entry(nxtfrag->list.prev, -+ struct mem_fragment, list); -+ nxtfrag->len += todel->len; -+ nxtfrag->pfn_len += todel->pfn_len; -+ list_del(&todel->list); -+ } -+ /* Re evaluate the list, things may merge now */ -+ frag = list_entry(mem_list.next, struct mem_fragment, list); -+ } -+} -+ -+/* Hook from arch/powerpc/mm/mem.c */ -+int usdpaa_test_fault(unsigned long pfn, u64 *phys_addr, u64 *size) -+{ -+ struct mem_fragment *frag; -+ int idx = -1; -+ if ((pfn < pfn_start) || (pfn >= (pfn_start + pfn_size))) -+ return -1; -+ /* It's in-range, we need to find the fragment */ -+ spin_lock(&mem_lock); -+ list_for_each_entry(frag, &mem_list, list) { -+ if ((pfn >= frag->pfn_base) && (pfn < (frag->pfn_base + -+ frag->pfn_len))) { -+ *phys_addr = frag->base; -+ *size = frag->len; -+ idx = current_tlb++; -+ if (current_tlb >= (first_tlb + num_tlb)) -+ current_tlb = first_tlb; -+ break; -+ } -+ } -+ spin_unlock(&mem_lock); -+ return idx; -+} -+ -+static int usdpaa_open(struct inode *inode, struct file *filp) -+{ -+ const struct alloc_backend *backend = &alloc_backends[0]; -+ struct ctx *ctx = kmalloc(sizeof(struct ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; -+ filp->private_data = ctx; -+ -+ while (backend->id_type != usdpaa_id_max) { -+ INIT_LIST_HEAD(&ctx->resources[backend->id_type]); -+ backend++; -+ } -+ -+ INIT_LIST_HEAD(&ctx->maps); -+ INIT_LIST_HEAD(&ctx->portals); -+ spin_lock_init(&ctx->lock); -+ -+ //filp->f_mapping->backing_dev_info = &directly_mappable_cdev_bdi; -+ -+ return 0; -+} -+ -+#define DQRR_MAXFILL 15 -+ -+/* Reset a QMan portal to its default state */ -+static int init_qm_portal(struct qm_portal_config *config, -+ struct qm_portal *portal) -+{ -+ const struct qm_dqrr_entry *dqrr = NULL; -+ int i; -+ -+ portal->addr.addr_ce = config->addr_virt[DPA_PORTAL_CE]; -+ portal->addr.addr_ci = config->addr_virt[DPA_PORTAL_CI]; -+ -+ /* Make sure interrupts are inhibited */ -+ qm_out(IIR, 1); -+ -+ /* Initialize the DQRR. This will stop any dequeue -+ commands that are in progress */ -+ if (qm_dqrr_init(portal, config, qm_dqrr_dpush, qm_dqrr_pvb, -+ qm_dqrr_cdc, DQRR_MAXFILL)) { -+ pr_err("qm_dqrr_init() failed when trying to" -+ " recover portal, portal will be leaked\n"); -+ return 1; -+ } -+ -+ /* Discard any entries on the DQRR */ -+ /* If we consume the ring twice something is wrong */ -+ for (i = 0; i < DQRR_MAXFILL * 2; i++) { -+ qm_dqrr_pvb_update(portal); -+ dqrr = qm_dqrr_current(portal); -+ if (!dqrr) -+ break; -+ qm_dqrr_cdc_consume_1ptr(portal, dqrr, 0); -+ qm_dqrr_pvb_update(portal); -+ qm_dqrr_next(portal); -+ } -+ /* Initialize the EQCR */ -+ if (qm_eqcr_init(portal, qm_eqcr_pvb, -+ qm_eqcr_get_ci_stashing(portal), 1)) { -+ pr_err("Qman EQCR initialisation failed\n"); -+ return 1; -+ } -+ /* initialize the MR */ -+ if (qm_mr_init(portal, qm_mr_pvb, qm_mr_cci)) { -+ pr_err("Qman MR initialisation failed\n"); -+ return 1; -+ } -+ qm_mr_pvb_update(portal); -+ while (qm_mr_current(portal)) { -+ qm_mr_next(portal); -+ qm_mr_cci_consume_to_current(portal); -+ qm_mr_pvb_update(portal); -+ } -+ -+ if (qm_mc_init(portal)) { -+ pr_err("Qman MC initialisation failed\n"); -+ return 1; -+ } -+ return 0; -+} -+ -+static int init_bm_portal(struct bm_portal_config *config, -+ struct bm_portal *portal) -+{ -+ portal->addr.addr_ce = config->addr_virt[DPA_PORTAL_CE]; -+ portal->addr.addr_ci = config->addr_virt[DPA_PORTAL_CI]; -+ -+ if (bm_rcr_init(portal, bm_rcr_pvb, bm_rcr_cce)) { -+ pr_err("Bman RCR initialisation failed\n"); -+ return 1; -+ } -+ if (bm_mc_init(portal)) { -+ pr_err("Bman MC initialisation failed\n"); -+ return 1; -+ } -+ return 0; -+} -+ -+/* Function that will scan all FQ's in the system. For each FQ that is not -+ OOS it will call the check_channel helper to determine if the FQ should -+ be torn down. If the check_channel helper returns true the FQ will be -+ transitioned to the OOS state */ -+static int qm_check_and_destroy_fqs(struct qm_portal *portal, void *ctx, -+ bool (*check_channel)(void*, u32)) -+{ -+ u32 fq_id = 0; -+ while (1) { -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ u8 state; -+ u32 channel; -+ -+ /* Determine the channel for the FQID */ -+ mcc = qm_mc_start(portal); -+ mcc->queryfq.fqid = fq_id; -+ qm_mc_commit(portal, QM_MCC_VERB_QUERYFQ); -+ while (!(mcr = qm_mc_result(portal))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) -+ == QM_MCR_VERB_QUERYFQ); -+ if (mcr->result != QM_MCR_RESULT_OK) -+ break; /* End of valid FQIDs */ -+ -+ channel = mcr->queryfq.fqd.dest.channel; -+ /* Determine the state of the FQID */ -+ mcc = qm_mc_start(portal); -+ mcc->queryfq_np.fqid = fq_id; -+ qm_mc_commit(portal, QM_MCC_VERB_QUERYFQ_NP); -+ while (!(mcr = qm_mc_result(portal))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) -+ == QM_MCR_VERB_QUERYFQ_NP); -+ state = mcr->queryfq_np.state & QM_MCR_NP_STATE_MASK; -+ if (state == QM_MCR_NP_STATE_OOS) -+ /* Already OOS, no need to do anymore checks */ -+ goto next; -+ -+ if (check_channel(ctx, channel)) -+ qm_shutdown_fq(&portal, 1, fq_id); -+ next: -+ ++fq_id; -+ } -+ return 0; -+} -+ -+static bool check_channel_device(void *_ctx, u32 channel) -+{ -+ struct ctx *ctx = _ctx; -+ struct portal_mapping *portal, *tmpportal; -+ struct active_resource *res; -+ -+ /* See if the FQ is destined for one of the portals we're cleaning up */ -+ list_for_each_entry_safe(portal, tmpportal, &ctx->portals, list) { -+ if (portal->user.type == usdpaa_portal_qman) { -+ if (portal->qportal->public_cfg.channel == channel) { -+ /* This FQs destination is a portal -+ we're cleaning, send a retire */ -+ return true; -+ } -+ } -+ } -+ -+ /* Check the pool channels that will be released as well */ -+ list_for_each_entry(res, &ctx->resources[usdpaa_id_qpool], list) { -+ if ((res->id >= channel) && -+ ((res->id + res->num - 1) <= channel)) -+ return true; -+ } -+ return false; -+} -+ -+static bool check_portal_channel(void *ctx, u32 channel) -+{ -+ u32 portal_channel = *(u32 *)ctx; -+ if (portal_channel == channel) { -+ /* This FQs destination is a portal -+ we're cleaning, send a retire */ -+ return true; -+ } -+ return false; -+} -+ -+ -+ -+ -+static int usdpaa_release(struct inode *inode, struct file *filp) -+{ -+ struct ctx *ctx = filp->private_data; -+ struct mem_mapping *map, *tmpmap; -+ struct portal_mapping *portal, *tmpportal; -+ const struct alloc_backend *backend = &alloc_backends[0]; -+ struct active_resource *res; -+ struct qm_portal *qm_cleanup_portal = NULL; -+ struct bm_portal *bm_cleanup_portal = NULL; -+ struct qm_portal_config *qm_alloced_portal = NULL; -+ struct bm_portal_config *bm_alloced_portal = NULL; -+ -+ struct qm_portal *portal_array[qman_portal_max]; -+ int portal_count = 0; -+ -+ /* Ensure the release operation cannot be migrated to another -+ CPU as CPU specific variables may be needed during cleanup */ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ migrate_disable(); -+#endif -+ /* The following logic is used to recover resources that were not -+ correctly released by the process that is closing the FD. -+ Step 1: syncronize the HW with the qm_portal/bm_portal structures -+ in the kernel -+ */ -+ -+ list_for_each_entry_safe(portal, tmpportal, &ctx->portals, list) { -+ /* Try to recover any portals that weren't shut down */ -+ if (portal->user.type == usdpaa_portal_qman) { -+ portal_array[portal_count] = &portal->qman_portal_low; -+ ++portal_count; -+ init_qm_portal(portal->qportal, -+ &portal->qman_portal_low); -+ if (!qm_cleanup_portal) { -+ qm_cleanup_portal = &portal->qman_portal_low; -+ } else { -+ /* Clean FQs on the dedicated channel */ -+ u32 chan = portal->qportal->public_cfg.channel; -+ qm_check_and_destroy_fqs( -+ &portal->qman_portal_low, &chan, -+ check_portal_channel); -+ } -+ } else { -+ /* BMAN */ -+ init_bm_portal(portal->bportal, -+ &portal->bman_portal_low); -+ if (!bm_cleanup_portal) -+ bm_cleanup_portal = &portal->bman_portal_low; -+ } -+ } -+ /* If no portal was found, allocate one for cleanup */ -+ if (!qm_cleanup_portal) { -+ qm_alloced_portal = qm_get_unused_portal(); -+ if (!qm_alloced_portal) { -+ pr_crit("No QMan portal avalaible for cleanup\n"); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ migrate_enable(); -+#endif -+ return -1; -+ } -+ qm_cleanup_portal = kmalloc(sizeof(struct qm_portal), -+ GFP_KERNEL); -+ if (!qm_cleanup_portal) { -+#ifdef CONFIG_PREEMPT_RT_FULL -+ migrate_enable(); -+#endif -+ return -ENOMEM; -+ } -+ init_qm_portal(qm_alloced_portal, qm_cleanup_portal); -+ portal_array[portal_count] = qm_cleanup_portal; -+ ++portal_count; -+ } -+ if (!bm_cleanup_portal) { -+ bm_alloced_portal = bm_get_unused_portal(); -+ if (!bm_alloced_portal) { -+ pr_crit("No BMan portal avalaible for cleanup\n"); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ migrate_enable(); -+#endif -+ return -1; -+ } -+ bm_cleanup_portal = kmalloc(sizeof(struct bm_portal), -+ GFP_KERNEL); -+ if (!bm_cleanup_portal) { -+#ifdef CONFIG_PREEMPT_RT_FULL -+ migrate_enable(); -+#endif -+ return -ENOMEM; -+ } -+ init_bm_portal(bm_alloced_portal, bm_cleanup_portal); -+ } -+ -+ /* OOS the FQs associated with this process */ -+ qm_check_and_destroy_fqs(qm_cleanup_portal, ctx, check_channel_device); -+ -+ while (backend->id_type != usdpaa_id_max) { -+ int leaks = 0; -+ list_for_each_entry(res, &ctx->resources[backend->id_type], -+ list) { -+ if (backend->id_type == usdpaa_id_fqid) { -+ int i = 0; -+ for (; i < res->num; i++) { -+ /* Clean FQs with the cleanup portal */ -+ qm_shutdown_fq(portal_array, -+ portal_count, -+ res->id + i); -+ } -+ } -+ leaks += res->num; -+ backend->release(res->id, res->num); -+ } -+ if (leaks) -+ pr_crit("USDPAA process leaking %d %s%s\n", leaks, -+ backend->acronym, (leaks > 1) ? "s" : ""); -+ backend++; -+ } -+ /* Release any DMA regions */ -+ spin_lock(&mem_lock); -+ list_for_each_entry_safe(map, tmpmap, &ctx->maps, list) { -+ struct mem_fragment *current_frag = map->root_frag; -+ int i; -+ if (map->root_frag->has_locking && -+ (map->root_frag->owner == map)) { -+ map->root_frag->owner = NULL; -+ wake_up(&map->root_frag->wq); -+ } -+ /* Check each fragment and merge if the ref count is 0 */ -+ for (i = 0; i < map->frag_count; i++) { -+ --current_frag->refs; -+ current_frag = list_entry(current_frag->list.prev, -+ struct mem_fragment, list); -+ } -+ -+ compress_frags(); -+ list_del(&map->list); -+ kfree(map); -+ } -+ spin_unlock(&mem_lock); -+ -+ /* Return portals */ -+ list_for_each_entry_safe(portal, tmpportal, &ctx->portals, list) { -+ if (portal->user.type == usdpaa_portal_qman) { -+ /* Give the portal back to the allocator */ -+ init_qm_portal(portal->qportal, -+ &portal->qman_portal_low); -+ qm_put_unused_portal(portal->qportal); -+ } else { -+ init_bm_portal(portal->bportal, -+ &portal->bman_portal_low); -+ bm_put_unused_portal(portal->bportal); -+ } -+ list_del(&portal->list); -+ kfree(portal); -+ } -+ if (qm_alloced_portal) { -+ qm_put_unused_portal(qm_alloced_portal); -+ kfree(qm_cleanup_portal); -+ } -+ if (bm_alloced_portal) { -+ bm_put_unused_portal(bm_alloced_portal); -+ kfree(bm_cleanup_portal); -+ } -+ -+ kfree(ctx); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ migrate_enable(); -+#endif -+ return 0; -+} -+ -+static int check_mmap_dma(struct ctx *ctx, struct vm_area_struct *vma, -+ int *match, unsigned long *pfn) -+{ -+ struct mem_mapping *map; -+ -+ list_for_each_entry(map, &ctx->maps, list) { -+ int i; -+ struct mem_fragment *frag = map->root_frag; -+ -+ for (i = 0; i < map->frag_count; i++) { -+ if (frag->pfn_base == vma->vm_pgoff) { -+ *match = 1; -+ *pfn = frag->pfn_base; -+ return 0; -+ } -+ frag = list_entry(frag->list.next, struct mem_fragment, -+ list); -+ } -+ } -+ *match = 0; -+ return 0; -+} -+ -+static int check_mmap_resource(struct resource *res, struct vm_area_struct *vma, -+ int *match, unsigned long *pfn) -+{ -+ *pfn = res->start >> PAGE_SHIFT; -+ if (*pfn == vma->vm_pgoff) { -+ *match = 1; -+ if ((vma->vm_end - vma->vm_start) != resource_size(res)) -+ return -EINVAL; -+ } else -+ *match = 0; -+ return 0; -+} -+ -+static int check_mmap_portal(struct ctx *ctx, struct vm_area_struct *vma, -+ int *match, unsigned long *pfn) -+{ -+ struct portal_mapping *portal; -+ int ret; -+ -+ list_for_each_entry(portal, &ctx->portals, list) { -+ ret = check_mmap_resource(&portal->phys[DPA_PORTAL_CE], vma, -+ match, pfn); -+ if (*match) { -+ vma->vm_page_prot = -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ pgprot_cached_ns(vma->vm_page_prot); -+#else -+ pgprot_cached_noncoherent(vma->vm_page_prot); -+#endif -+ return ret; -+ } -+ ret = check_mmap_resource(&portal->phys[DPA_PORTAL_CI], vma, -+ match, pfn); -+ if (*match) { -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ return ret; -+ } -+ } -+ *match = 0; -+ return 0; -+} -+ -+static int usdpaa_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ struct ctx *ctx = filp->private_data; -+ unsigned long pfn = 0; -+ int match, ret; -+ -+ spin_lock(&mem_lock); -+ ret = check_mmap_dma(ctx, vma, &match, &pfn); -+ if (!match) -+ ret = check_mmap_portal(ctx, vma, &match, &pfn); -+ spin_unlock(&mem_lock); -+ if (!match) -+ return -EINVAL; -+ if (!ret) -+ ret = remap_pfn_range(vma, vma->vm_start, pfn, -+ vma->vm_end - vma->vm_start, -+ vma->vm_page_prot); -+ return ret; -+} -+ -+/* Return the nearest rounded-up address >= 'addr' that is 'sz'-aligned. 'sz' -+ * must be a power of 2, but both 'addr' and 'sz' can be expressions. */ -+#define USDPAA_MEM_ROUNDUP(addr, sz) \ -+ ({ \ -+ unsigned long foo_align = (sz) - 1; \ -+ ((addr) + foo_align) & ~foo_align; \ -+ }) -+/* Searching for a size-aligned virtual address range starting from 'addr' */ -+static unsigned long usdpaa_get_unmapped_area(struct file *file, -+ unsigned long addr, -+ unsigned long len, -+ unsigned long pgoff, -+ unsigned long flags) -+{ -+ struct vm_area_struct *vma; -+ -+ if (len % PAGE_SIZE) -+ return -EINVAL; -+ if (!len) -+ return -EINVAL; -+ -+ /* Need to align the address to the largest pagesize of the mapping -+ * because the MMU requires the virtual address to have the same -+ * alignment as the physical address */ -+ addr = USDPAA_MEM_ROUNDUP(addr, largest_page_size(len)); -+ vma = find_vma(current->mm, addr); -+ /* Keep searching until we reach the end of currently-used virtual -+ * address-space or we find a big enough gap. */ -+ while (vma) { -+ if ((addr + len) < vma->vm_start) -+ return addr; -+ -+ addr = USDPAA_MEM_ROUNDUP(vma->vm_end, largest_page_size(len)); -+ vma = vma->vm_next; -+ } -+ if ((TASK_SIZE - len) < addr) -+ return -ENOMEM; -+ return addr; -+} -+ -+static long ioctl_id_alloc(struct ctx *ctx, void __user *arg) -+{ -+ struct usdpaa_ioctl_id_alloc i; -+ const struct alloc_backend *backend; -+ struct active_resource *res; -+ int ret = copy_from_user(&i, arg, sizeof(i)); -+ if (ret) -+ return ret; -+ if ((i.id_type >= usdpaa_id_max) || !i.num) -+ return -EINVAL; -+ backend = &alloc_backends[i.id_type]; -+ /* Allocate the required resource type */ -+ ret = backend->alloc(&i.base, i.num, i.align, i.partial); -+ if (ret < 0) -+ return ret; -+ i.num = ret; -+ /* Copy the result to user-space */ -+ ret = copy_to_user(arg, &i, sizeof(i)); -+ if (ret) { -+ backend->release(i.base, i.num); -+ return ret; -+ } -+ /* Assign the allocated range to the FD accounting */ -+ res = kmalloc(sizeof(*res), GFP_KERNEL); -+ if (!res) { -+ backend->release(i.base, i.num); -+ return -ENOMEM; -+ } -+ spin_lock(&ctx->lock); -+ res->id = i.base; -+ res->num = i.num; -+ res->refcount = 1; -+ list_add(&res->list, &ctx->resources[i.id_type]); -+ spin_unlock(&ctx->lock); -+ return 0; -+} -+ -+static long ioctl_id_release(struct ctx *ctx, void __user *arg) -+{ -+ struct usdpaa_ioctl_id_release i; -+ const struct alloc_backend *backend; -+ struct active_resource *tmp, *pos; -+ -+ int ret = copy_from_user(&i, arg, sizeof(i)); -+ if (ret) -+ return ret; -+ if ((i.id_type >= usdpaa_id_max) || !i.num) -+ return -EINVAL; -+ backend = &alloc_backends[i.id_type]; -+ /* Pull the range out of the FD accounting - the range is valid iff this -+ * succeeds. */ -+ spin_lock(&ctx->lock); -+ list_for_each_entry_safe(pos, tmp, &ctx->resources[i.id_type], list) { -+ if (pos->id == i.base && pos->num == i.num) { -+ pos->refcount--; -+ if (pos->refcount) { -+ spin_unlock(&ctx->lock); -+ return 0; /* Still being used */ -+ } -+ list_del(&pos->list); -+ kfree(pos); -+ spin_unlock(&ctx->lock); -+ goto found; -+ } -+ } -+ /* Failed to find the resource */ -+ spin_unlock(&ctx->lock); -+ pr_err("Couldn't find resource type %d base 0x%x num %d\n", -+ i.id_type, i.base, i.num); -+ return -EINVAL; -+found: -+ /* Release the resource to the backend */ -+ backend->release(i.base, i.num); -+ return 0; -+} -+ -+static long ioctl_id_reserve(struct ctx *ctx, void __user *arg) -+{ -+ struct usdpaa_ioctl_id_reserve i; -+ const struct alloc_backend *backend; -+ struct active_resource *tmp, *pos; -+ -+ int ret = copy_from_user(&i, arg, sizeof(i)); -+ if (ret) -+ return ret; -+ if ((i.id_type >= usdpaa_id_max) || !i.num) -+ return -EINVAL; -+ backend = &alloc_backends[i.id_type]; -+ if (!backend->reserve) -+ return -EINVAL; -+ /* Pull the range out of the FD accounting - the range is valid iff this -+ * succeeds. */ -+ spin_lock(&ctx->lock); -+ list_for_each_entry_safe(pos, tmp, &ctx->resources[i.id_type], list) { -+ if (pos->id == i.base && pos->num == i.num) { -+ pos->refcount++; -+ spin_unlock(&ctx->lock); -+ return 0; -+ } -+ } -+ -+ /* Failed to find the resource */ -+ spin_unlock(&ctx->lock); -+ -+ /* Reserve the resource in the backend */ -+ ret = backend->reserve(i.base, i.num); -+ if (ret) -+ return ret; -+ /* Assign the reserved range to the FD accounting */ -+ pos = kmalloc(sizeof(*pos), GFP_KERNEL); -+ if (!pos) { -+ backend->release(i.base, i.num); -+ return -ENOMEM; -+ } -+ spin_lock(&ctx->lock); -+ pos->id = i.base; -+ pos->num = i.num; -+ pos->refcount = 1; -+ list_add(&pos->list, &ctx->resources[i.id_type]); -+ spin_unlock(&ctx->lock); -+ return 0; -+} -+ -+static long ioctl_dma_map(struct file *fp, struct ctx *ctx, -+ struct usdpaa_ioctl_dma_map *i) -+{ -+ struct mem_fragment *frag, *start_frag, *next_frag; -+ struct mem_mapping *map, *tmp; -+ int ret = 0; -+ u32 largest_page, so_far = 0; -+ int frag_count = 0; -+ unsigned long next_addr = PAGE_SIZE, populate; -+ -+ /* error checking to ensure values copied from user space are valid */ -+ if (i->len % PAGE_SIZE) -+ return -EINVAL; -+ -+ map = kmalloc(sizeof(*map), GFP_KERNEL); -+ if (!map) -+ return -ENOMEM; -+ -+ spin_lock(&mem_lock); -+ if (i->flags & USDPAA_DMA_FLAG_SHARE) { -+ list_for_each_entry(frag, &mem_list, list) { -+ if (frag->refs && (frag->flags & -+ USDPAA_DMA_FLAG_SHARE) && -+ !strncmp(i->name, frag->name, -+ USDPAA_DMA_NAME_MAX)) { -+ /* Matching entry */ -+ if ((i->flags & USDPAA_DMA_FLAG_CREATE) && -+ !(i->flags & USDPAA_DMA_FLAG_LAZY)) { -+ ret = -EBUSY; -+ goto out; -+ } -+ -+ /* Check to ensure size matches record */ -+ if (i->len != frag->map_len && i->len) { -+ pr_err("ioctl_dma_map() Size requested does not match %s and is none zero\n", -+ frag->name); -+ return -EINVAL; -+ } -+ -+ /* Check if this has already been mapped -+ to this process */ -+ list_for_each_entry(tmp, &ctx->maps, list) -+ if (tmp->root_frag == frag) { -+ /* Already mapped, just need to -+ inc ref count */ -+ tmp->refs++; -+ kfree(map); -+ i->did_create = 0; -+ i->len = tmp->total_size; -+ i->phys_addr = frag->base; -+ i->ptr = tmp->virt_addr; -+ spin_unlock(&mem_lock); -+ return 0; -+ } -+ /* Matching entry - just need to map */ -+ i->has_locking = frag->has_locking; -+ i->did_create = 0; -+ i->len = frag->map_len; -+ start_frag = frag; -+ goto do_map; -+ } -+ } -+ /* No matching entry */ -+ if (!(i->flags & USDPAA_DMA_FLAG_CREATE)) { -+ pr_err("ioctl_dma_map() No matching entry\n"); -+ ret = -ENOMEM; -+ goto out; -+ } -+ } -+ /* New fragment required, size must be provided. */ -+ if (!i->len) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ /* Find one of more contiguous fragments that satisfy the total length -+ trying to minimize the number of fragments -+ compute the largest page size that the allocation could use */ -+ largest_page = largest_page_size(i->len); -+ start_frag = NULL; -+ while (largest_page && -+ largest_page <= largest_page_size(phys_size) && -+ start_frag == NULL) { -+ /* Search the list for a frag of that size */ -+ list_for_each_entry(frag, &mem_list, list) { -+ if (!frag->refs && (frag->len == largest_page)) { -+ /* See if the next x fragments are free -+ and can accomidate the size */ -+ u32 found_size = largest_page; -+ next_frag = list_entry(frag->list.prev, -+ struct mem_fragment, -+ list); -+ /* If the fragement is too small check -+ if the neighbours cab support it */ -+ while (found_size < i->len) { -+ if (&mem_list == &next_frag->list) -+ break; /* End of list */ -+ if (next_frag->refs != 0 || -+ next_frag->len == 0) -+ break; /* not enough space */ -+ found_size += next_frag->len; -+ next_frag = list_entry( -+ next_frag->list.prev, -+ struct mem_fragment, -+ list); -+ } -+ if (found_size >= i->len) { -+ /* Success! there is enough contigous -+ free space */ -+ start_frag = frag; -+ break; -+ } -+ } -+ } /* next frag loop */ -+ /* Couldn't statisfy the request with this -+ largest page size, try a smaller one */ -+ largest_page <<= 2; -+ } -+ if (start_frag == NULL) { -+ /* Couldn't find proper amount of space */ -+ ret = -ENOMEM; -+ goto out; -+ } -+ i->did_create = 1; -+do_map: -+ /* Verify there is sufficient space to do the mapping */ -+ down_write(¤t->mm->mmap_sem); -+ next_addr = usdpaa_get_unmapped_area(fp, next_addr, i->len, 0, 0); -+ up_write(¤t->mm->mmap_sem); -+ -+ if (next_addr & ~PAGE_MASK) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ /* We may need to divide the final fragment to accomidate the mapping */ -+ next_frag = start_frag; -+ while (so_far != i->len) { -+ BUG_ON(next_frag->len == 0); -+ while ((next_frag->len + so_far) > i->len) { -+ /* Split frag until they match */ -+ split_frag(next_frag); -+ } -+ so_far += next_frag->len; -+ next_frag->refs++; -+ ++frag_count; -+ next_frag = list_entry(next_frag->list.prev, -+ struct mem_fragment, list); -+ } -+ if (i->did_create) { -+ size_t name_len = 0; -+ start_frag->flags = i->flags; -+ strncpy(start_frag->name, i->name, USDPAA_DMA_NAME_MAX); -+ name_len = strnlen(start_frag->name, USDPAA_DMA_NAME_MAX); -+ if (name_len >= USDPAA_DMA_NAME_MAX) { -+ ret = -EFAULT; -+ goto out; -+ } -+ start_frag->map_len = i->len; -+ start_frag->has_locking = i->has_locking; -+ init_waitqueue_head(&start_frag->wq); -+ start_frag->owner = NULL; -+ } -+ -+ /* Setup the map entry */ -+ map->root_frag = start_frag; -+ map->total_size = i->len; -+ map->frag_count = frag_count; -+ map->refs = 1; -+ list_add(&map->list, &ctx->maps); -+ i->phys_addr = start_frag->base; -+out: -+ spin_unlock(&mem_lock); -+ -+ if (!ret) { -+ unsigned long longret; -+ down_write(¤t->mm->mmap_sem); -+ longret = do_mmap_pgoff(fp, next_addr, map->total_size, -+ PROT_READ | -+ (i->flags & -+ USDPAA_DMA_FLAG_RDONLY ? 0 -+ : PROT_WRITE), -+ MAP_SHARED, -+ start_frag->pfn_base, -+ &populate, -+ NULL); -+ up_write(¤t->mm->mmap_sem); -+ if (longret & ~PAGE_MASK) { -+ ret = (int)longret; -+ } else { -+ i->ptr = (void *)longret; -+ map->virt_addr = i->ptr; -+ } -+ } else -+ kfree(map); -+ return ret; -+} -+ -+static long ioctl_dma_unmap(struct ctx *ctx, void __user *arg) -+{ -+ struct mem_mapping *map; -+ struct vm_area_struct *vma; -+ int ret, i; -+ struct mem_fragment *current_frag; -+ size_t sz; -+ unsigned long base; -+ unsigned long vaddr; -+ -+ down_write(¤t->mm->mmap_sem); -+ vma = find_vma(current->mm, (unsigned long)arg); -+ if (!vma || (vma->vm_start > (unsigned long)arg)) { -+ up_write(¤t->mm->mmap_sem); -+ return -EFAULT; -+ } -+ spin_lock(&mem_lock); -+ list_for_each_entry(map, &ctx->maps, list) { -+ if (map->root_frag->pfn_base == vma->vm_pgoff) { -+ /* Drop the map lock if we hold it */ -+ if (map->root_frag->has_locking && -+ (map->root_frag->owner == map)) { -+ map->root_frag->owner = NULL; -+ wake_up(&map->root_frag->wq); -+ } -+ goto map_match; -+ } -+ } -+ /* Failed to find a matching mapping for this process */ -+ ret = -EFAULT; -+ spin_unlock(&mem_lock); -+ goto out; -+map_match: -+ map->refs--; -+ if (map->refs != 0) { -+ /* Another call the dma_map is referencing this */ -+ ret = 0; -+ spin_unlock(&mem_lock); -+ goto out; -+ } -+ -+ current_frag = map->root_frag; -+ vaddr = (unsigned long) map->virt_addr; -+ for (i = 0; i < map->frag_count; i++) { -+ DPA_ASSERT(current_frag->refs > 0); -+ --current_frag->refs; -+#if !(defined(CONFIG_ARM) || defined(CONFIG_ARM64)) -+ /* -+ * Make sure we invalidate the TLB entry for -+ * this fragment, otherwise a remap of a different -+ * page to this vaddr would give acces to an -+ * incorrect piece of memory -+ */ -+ cleartlbcam(vaddr, mfspr(SPRN_PID)); -+#endif -+ vaddr += current_frag->len; -+ current_frag = list_entry(current_frag->list.prev, -+ struct mem_fragment, list); -+ } -+ map->root_frag->name[0] = 0; -+ list_del(&map->list); -+ compress_frags(); -+ spin_unlock(&mem_lock); -+ -+ base = vma->vm_start; -+ sz = vma->vm_end - vma->vm_start; -+ do_munmap(current->mm, base, sz, NULL); -+ ret = 0; -+ out: -+ up_write(¤t->mm->mmap_sem); -+ return ret; -+} -+ -+static long ioctl_dma_stats(struct ctx *ctx, void __user *arg) -+{ -+ struct mem_fragment *frag; -+ struct usdpaa_ioctl_dma_used result; -+ -+ result.free_bytes = 0; -+ result.total_bytes = phys_size; -+ -+ list_for_each_entry(frag, &mem_list, list) { -+ if (frag->refs == 0) -+ result.free_bytes += frag->len; -+ } -+ -+ return copy_to_user(arg, &result, sizeof(result)); } -+ -+static int test_lock(struct mem_mapping *map) -+{ -+ int ret = 0; -+ spin_lock(&mem_lock); -+ if (!map->root_frag->owner) { -+ map->root_frag->owner = map; -+ ret = 1; -+ } -+ spin_unlock(&mem_lock); -+ return ret; -+} -+ -+static long ioctl_dma_lock(struct ctx *ctx, void __user *arg) -+{ -+ struct mem_mapping *map; -+ struct vm_area_struct *vma; -+ -+ down_read(¤t->mm->mmap_sem); -+ vma = find_vma(current->mm, (unsigned long)arg); -+ if (!vma || (vma->vm_start > (unsigned long)arg)) { -+ up_read(¤t->mm->mmap_sem); -+ return -EFAULT; -+ } -+ spin_lock(&mem_lock); -+ list_for_each_entry(map, &ctx->maps, list) { -+ if (map->root_frag->pfn_base == vma->vm_pgoff) -+ goto map_match; -+ } -+ map = NULL; -+map_match: -+ spin_unlock(&mem_lock); -+ up_read(¤t->mm->mmap_sem); -+ -+ if (!map) -+ return -EFAULT; -+ if (!map->root_frag->has_locking) -+ return -ENODEV; -+ return wait_event_interruptible(map->root_frag->wq, test_lock(map)); -+} -+ -+static long ioctl_dma_unlock(struct ctx *ctx, void __user *arg) -+{ -+ struct mem_mapping *map; -+ struct vm_area_struct *vma; -+ int ret; -+ -+ down_read(¤t->mm->mmap_sem); -+ vma = find_vma(current->mm, (unsigned long)arg); -+ if (!vma || (vma->vm_start > (unsigned long)arg)) -+ ret = -EFAULT; -+ else { -+ spin_lock(&mem_lock); -+ list_for_each_entry(map, &ctx->maps, list) { -+ if (map->root_frag->pfn_base == vma->vm_pgoff) { -+ if (!map->root_frag->has_locking) -+ ret = -ENODEV; -+ else if (map->root_frag->owner == map) { -+ map->root_frag->owner = NULL; -+ wake_up(&map->root_frag->wq); -+ ret = 0; -+ } else -+ ret = -EBUSY; -+ goto map_match; -+ } -+ } -+ ret = -EINVAL; -+map_match: -+ spin_unlock(&mem_lock); -+ } -+ up_read(¤t->mm->mmap_sem); -+ return ret; -+} -+ -+static int portal_mmap(struct file *fp, struct resource *res, void **ptr) -+{ -+ unsigned long longret = 0, populate; -+ resource_size_t len; -+ -+ down_write(¤t->mm->mmap_sem); -+ len = resource_size(res); -+ if (len != (unsigned long)len) -+ return -EINVAL; -+ longret = do_mmap_pgoff(fp, PAGE_SIZE, (unsigned long)len, -+ PROT_READ | PROT_WRITE, MAP_SHARED, -+ res->start >> PAGE_SHIFT, &populate, NULL); -+ up_write(¤t->mm->mmap_sem); -+ -+ if (longret & ~PAGE_MASK) -+ return (int)longret; -+ -+ *ptr = (void *) longret; -+ return 0; -+} -+ -+static void portal_munmap(struct resource *res, void *ptr) -+{ -+ down_write(¤t->mm->mmap_sem); -+ do_munmap(current->mm, (unsigned long)ptr, resource_size(res), NULL); -+ up_write(¤t->mm->mmap_sem); -+} -+ -+static long ioctl_portal_map(struct file *fp, struct ctx *ctx, -+ struct usdpaa_ioctl_portal_map *arg) -+{ -+ struct portal_mapping *mapping = kmalloc(sizeof(*mapping), GFP_KERNEL); -+ int ret; -+ -+ if (!mapping) -+ return -ENOMEM; -+ -+ mapping->user = *arg; -+ mapping->iommu_domain = NULL; -+ -+ if (mapping->user.type == usdpaa_portal_qman) { -+ mapping->qportal = -+ qm_get_unused_portal_idx(mapping->user.index); -+ if (!mapping->qportal) { -+ ret = -ENODEV; -+ goto err_get_portal; -+ } -+ mapping->phys = &mapping->qportal->addr_phys[0]; -+ mapping->user.channel = mapping->qportal->public_cfg.channel; -+ mapping->user.pools = mapping->qportal->public_cfg.pools; -+ mapping->user.index = mapping->qportal->public_cfg.index; -+ } else if (mapping->user.type == usdpaa_portal_bman) { -+ mapping->bportal = -+ bm_get_unused_portal_idx(mapping->user.index); -+ if (!mapping->bportal) { -+ ret = -ENODEV; -+ goto err_get_portal; -+ } -+ mapping->phys = &mapping->bportal->addr_phys[0]; -+ mapping->user.index = mapping->bportal->public_cfg.index; -+ } else { -+ ret = -EINVAL; -+ goto err_copy_from_user; -+ } -+ /* Need to put pcfg in ctx's list before the mmaps because the mmap -+ * handlers look it up. */ -+ spin_lock(&mem_lock); -+ list_add(&mapping->list, &ctx->portals); -+ spin_unlock(&mem_lock); -+ ret = portal_mmap(fp, &mapping->phys[DPA_PORTAL_CE], -+ &mapping->user.addr.cena); -+ if (ret) -+ goto err_mmap_cena; -+ ret = portal_mmap(fp, &mapping->phys[DPA_PORTAL_CI], -+ &mapping->user.addr.cinh); -+ if (ret) -+ goto err_mmap_cinh; -+ *arg = mapping->user; -+ return ret; -+ -+err_mmap_cinh: -+ portal_munmap(&mapping->phys[DPA_PORTAL_CE], mapping->user.addr.cena); -+err_mmap_cena: -+ if ((mapping->user.type == usdpaa_portal_qman) && mapping->qportal) -+ qm_put_unused_portal(mapping->qportal); -+ else if ((mapping->user.type == usdpaa_portal_bman) && mapping->bportal) -+ bm_put_unused_portal(mapping->bportal); -+ spin_lock(&mem_lock); -+ list_del(&mapping->list); -+ spin_unlock(&mem_lock); -+err_get_portal: -+err_copy_from_user: -+ kfree(mapping); -+ return ret; -+} -+ -+static long ioctl_portal_unmap(struct ctx *ctx, struct usdpaa_portal_map *i) -+{ -+ struct portal_mapping *mapping; -+ struct vm_area_struct *vma; -+ unsigned long pfn; -+ u32 channel; -+ -+ /* Get the PFN corresponding to one of the virt addresses */ -+ down_read(¤t->mm->mmap_sem); -+ vma = find_vma(current->mm, (unsigned long)i->cinh); -+ if (!vma || (vma->vm_start > (unsigned long)i->cinh)) { -+ up_read(¤t->mm->mmap_sem); -+ return -EFAULT; -+ } -+ pfn = vma->vm_pgoff; -+ up_read(¤t->mm->mmap_sem); -+ -+ /* Find the corresponding portal */ -+ spin_lock(&mem_lock); -+ list_for_each_entry(mapping, &ctx->portals, list) { -+ if (pfn == (mapping->phys[DPA_PORTAL_CI].start >> PAGE_SHIFT)) -+ goto found; -+ } -+ mapping = NULL; -+found: -+ if (mapping) -+ list_del(&mapping->list); -+ spin_unlock(&mem_lock); -+ if (!mapping) -+ return -ENODEV; -+ portal_munmap(&mapping->phys[DPA_PORTAL_CI], mapping->user.addr.cinh); -+ portal_munmap(&mapping->phys[DPA_PORTAL_CE], mapping->user.addr.cena); -+ if (mapping->user.type == usdpaa_portal_qman) { -+ init_qm_portal(mapping->qportal, -+ &mapping->qman_portal_low); -+ -+ /* Tear down any FQs this portal is referencing */ -+ channel = mapping->qportal->public_cfg.channel; -+ qm_check_and_destroy_fqs(&mapping->qman_portal_low, -+ &channel, -+ check_portal_channel); -+ qm_put_unused_portal(mapping->qportal); -+ } else if (mapping->user.type == usdpaa_portal_bman) { -+ init_bm_portal(mapping->bportal, -+ &mapping->bman_portal_low); -+ bm_put_unused_portal(mapping->bportal); -+ } -+ kfree(mapping); -+ return 0; -+} -+ -+static void portal_config_pamu(struct qm_portal_config *pcfg, uint8_t sdest, -+ uint32_t cpu, uint32_t cache, uint32_t window) -+{ -+#ifdef CONFIG_FSL_PAMU -+ int ret; -+ int window_count = 1; -+ struct iommu_domain_geometry geom_attr; -+ struct pamu_stash_attribute stash_attr; -+ -+ pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type); -+ if (!pcfg->iommu_domain) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_alloc() failed", -+ __func__); -+ goto _no_iommu; -+ } -+ geom_attr.aperture_start = 0; -+ geom_attr.aperture_end = -+ ((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1; -+ geom_attr.force_aperture = true; -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY, -+ &geom_attr); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS, -+ &window_count); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ stash_attr.cpu = cpu; -+ stash_attr.cache = cache; -+ /* set stash information for the window */ -+ stash_attr.window = 0; -+ -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, -+ DOMAIN_ATTR_FSL_PAMU_STASH, -+ &stash_attr); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36, -+ IOMMU_READ | IOMMU_WRITE); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_window_enable() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ ret = iommu_attach_device(pcfg->iommu_domain, &pcfg->dev); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_device_attach() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, -+ DOMAIN_ATTR_FSL_PAMU_ENABLE, -+ &window_count); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d", -+ __func__, ret); -+ goto _iommu_detach_device; -+ } -+_no_iommu: -+#endif -+ -+#ifdef CONFIG_FSL_QMAN_CONFIG -+ if (qman_set_sdest(pcfg->public_cfg.channel, sdest)) -+#endif -+ pr_warn("Failed to set QMan portal's stash request queue\n"); -+ -+ return; -+ -+#ifdef CONFIG_FSL_PAMU -+_iommu_detach_device: -+ iommu_detach_device(pcfg->iommu_domain, NULL); -+_iommu_domain_free: -+ iommu_domain_free(pcfg->iommu_domain); -+#endif -+} -+ -+static long ioctl_allocate_raw_portal(struct file *fp, struct ctx *ctx, -+ struct usdpaa_ioctl_raw_portal *arg) -+{ -+ struct portal_mapping *mapping = kmalloc(sizeof(*mapping), GFP_KERNEL); -+ int ret; -+ -+ if (!mapping) -+ return -ENOMEM; -+ -+ mapping->user.type = arg->type; -+ mapping->iommu_domain = NULL; -+ if (arg->type == usdpaa_portal_qman) { -+ mapping->qportal = qm_get_unused_portal_idx(arg->index); -+ if (!mapping->qportal) { -+ ret = -ENODEV; -+ goto err; -+ } -+ mapping->phys = &mapping->qportal->addr_phys[0]; -+ arg->index = mapping->qportal->public_cfg.index; -+ arg->cinh = mapping->qportal->addr_phys[DPA_PORTAL_CI].start; -+ arg->cena = mapping->qportal->addr_phys[DPA_PORTAL_CE].start; -+ if (arg->enable_stash) { -+ /* Setup the PAMU with the supplied parameters */ -+ portal_config_pamu(mapping->qportal, arg->sdest, -+ arg->cpu, arg->cache, arg->window); -+ } -+ } else if (mapping->user.type == usdpaa_portal_bman) { -+ mapping->bportal = -+ bm_get_unused_portal_idx(arg->index); -+ if (!mapping->bportal) { -+ ret = -ENODEV; -+ goto err; -+ } -+ mapping->phys = &mapping->bportal->addr_phys[0]; -+ arg->index = mapping->bportal->public_cfg.index; -+ arg->cinh = mapping->bportal->addr_phys[DPA_PORTAL_CI].start; -+ arg->cena = mapping->bportal->addr_phys[DPA_PORTAL_CE].start; -+ } else { -+ ret = -EINVAL; -+ goto err; -+ } -+ /* Need to put pcfg in ctx's list before the mmaps because the mmap -+ * handlers look it up. */ -+ spin_lock(&mem_lock); -+ list_add(&mapping->list, &ctx->portals); -+ spin_unlock(&mem_lock); -+ return 0; -+err: -+ kfree(mapping); -+ return ret; -+} -+ -+static long ioctl_free_raw_portal(struct file *fp, struct ctx *ctx, -+ struct usdpaa_ioctl_raw_portal *arg) -+{ -+ struct portal_mapping *mapping; -+ u32 channel; -+ -+ /* Find the corresponding portal */ -+ spin_lock(&mem_lock); -+ list_for_each_entry(mapping, &ctx->portals, list) { -+ if (mapping->phys[DPA_PORTAL_CI].start == arg->cinh) -+ goto found; -+ } -+ mapping = NULL; -+found: -+ if (mapping) -+ list_del(&mapping->list); -+ spin_unlock(&mem_lock); -+ if (!mapping) -+ return -ENODEV; -+ if (mapping->user.type == usdpaa_portal_qman) { -+ init_qm_portal(mapping->qportal, -+ &mapping->qman_portal_low); -+ -+ /* Tear down any FQs this portal is referencing */ -+ channel = mapping->qportal->public_cfg.channel; -+ qm_check_and_destroy_fqs(&mapping->qman_portal_low, -+ &channel, -+ check_portal_channel); -+ qm_put_unused_portal(mapping->qportal); -+ } else if (mapping->user.type == usdpaa_portal_bman) { -+ init_bm_portal(mapping->bportal, -+ &mapping->bman_portal_low); -+ bm_put_unused_portal(mapping->bportal); -+ } -+ kfree(mapping); -+ return 0; -+} -+ -+static long usdpaa_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -+{ -+ struct ctx *ctx = fp->private_data; -+ void __user *a = (void __user *)arg; -+ switch (cmd) { -+ case USDPAA_IOCTL_ID_ALLOC: -+ return ioctl_id_alloc(ctx, a); -+ case USDPAA_IOCTL_ID_RELEASE: -+ return ioctl_id_release(ctx, a); -+ case USDPAA_IOCTL_ID_RESERVE: -+ return ioctl_id_reserve(ctx, a); -+ case USDPAA_IOCTL_DMA_MAP: -+ { -+ struct usdpaa_ioctl_dma_map input; -+ int ret; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ ret = ioctl_dma_map(fp, ctx, &input); -+ if (copy_to_user(a, &input, sizeof(input))) -+ return -EFAULT; -+ return ret; -+ } -+ case USDPAA_IOCTL_DMA_UNMAP: -+ return ioctl_dma_unmap(ctx, a); -+ case USDPAA_IOCTL_DMA_LOCK: -+ return ioctl_dma_lock(ctx, a); -+ case USDPAA_IOCTL_DMA_UNLOCK: -+ return ioctl_dma_unlock(ctx, a); -+ case USDPAA_IOCTL_PORTAL_MAP: -+ { -+ struct usdpaa_ioctl_portal_map input; -+ int ret; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ ret = ioctl_portal_map(fp, ctx, &input); -+ if (copy_to_user(a, &input, sizeof(input))) -+ return -EFAULT; -+ return ret; -+ } -+ case USDPAA_IOCTL_PORTAL_UNMAP: -+ { -+ struct usdpaa_portal_map input; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ return ioctl_portal_unmap(ctx, &input); -+ } -+ case USDPAA_IOCTL_DMA_USED: -+ return ioctl_dma_stats(ctx, a); -+ case USDPAA_IOCTL_ALLOC_RAW_PORTAL: -+ { -+ struct usdpaa_ioctl_raw_portal input; -+ int ret; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ ret = ioctl_allocate_raw_portal(fp, ctx, &input); -+ if (copy_to_user(a, &input, sizeof(input))) -+ return -EFAULT; -+ return ret; -+ } -+ case USDPAA_IOCTL_FREE_RAW_PORTAL: -+ { -+ struct usdpaa_ioctl_raw_portal input; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ return ioctl_free_raw_portal(fp, ctx, &input); -+ } -+ } -+ return -EINVAL; -+} -+ -+static long usdpaa_ioctl_compat(struct file *fp, unsigned int cmd, -+ unsigned long arg) -+{ -+#ifdef CONFIG_COMPAT -+ struct ctx *ctx = fp->private_data; -+ void __user *a = (void __user *)arg; -+#endif -+ switch (cmd) { -+#ifdef CONFIG_COMPAT -+ case USDPAA_IOCTL_DMA_MAP_COMPAT: -+ { -+ int ret; -+ struct usdpaa_ioctl_dma_map_compat input; -+ struct usdpaa_ioctl_dma_map converted; -+ -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ -+ converted.ptr = compat_ptr(input.ptr); -+ converted.phys_addr = input.phys_addr; -+ converted.len = input.len; -+ converted.flags = input.flags; -+ strncpy(converted.name, input.name, USDPAA_DMA_NAME_MAX); -+ converted.has_locking = input.has_locking; -+ converted.did_create = input.did_create; -+ -+ ret = ioctl_dma_map(fp, ctx, &converted); -+ input.ptr = ptr_to_compat(converted.ptr); -+ input.phys_addr = converted.phys_addr; -+ input.len = converted.len; -+ input.flags = converted.flags; -+ strncpy(input.name, converted.name, USDPAA_DMA_NAME_MAX); -+ input.has_locking = converted.has_locking; -+ input.did_create = converted.did_create; -+ if (copy_to_user(a, &input, sizeof(input))) -+ return -EFAULT; -+ return ret; -+ } -+ case USDPAA_IOCTL_PORTAL_MAP_COMPAT: -+ { -+ int ret; -+ struct compat_usdpaa_ioctl_portal_map input; -+ struct usdpaa_ioctl_portal_map converted; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ converted.type = input.type; -+ converted.index = input.index; -+ ret = ioctl_portal_map(fp, ctx, &converted); -+ input.addr.cinh = ptr_to_compat(converted.addr.cinh); -+ input.addr.cena = ptr_to_compat(converted.addr.cena); -+ input.channel = converted.channel; -+ input.pools = converted.pools; -+ input.index = converted.index; -+ if (copy_to_user(a, &input, sizeof(input))) -+ return -EFAULT; -+ return ret; -+ } -+ case USDPAA_IOCTL_PORTAL_UNMAP_COMPAT: -+ { -+ struct usdpaa_portal_map_compat input; -+ struct usdpaa_portal_map converted; -+ -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ converted.cinh = compat_ptr(input.cinh); -+ converted.cena = compat_ptr(input.cena); -+ return ioctl_portal_unmap(ctx, &converted); -+ } -+ case USDPAA_IOCTL_ALLOC_RAW_PORTAL_COMPAT: -+ { -+ int ret; -+ struct usdpaa_ioctl_raw_portal converted; -+ struct compat_ioctl_raw_portal input; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ converted.type = input.type; -+ converted.index = input.index; -+ converted.enable_stash = input.enable_stash; -+ converted.cpu = input.cpu; -+ converted.cache = input.cache; -+ converted.window = input.window; -+ converted.sdest = input.sdest; -+ ret = ioctl_allocate_raw_portal(fp, ctx, &converted); -+ -+ input.cinh = converted.cinh; -+ input.cena = converted.cena; -+ input.index = converted.index; -+ -+ if (copy_to_user(a, &input, sizeof(input))) -+ return -EFAULT; -+ return ret; -+ } -+ case USDPAA_IOCTL_FREE_RAW_PORTAL_COMPAT: -+ { -+ struct usdpaa_ioctl_raw_portal converted; -+ struct compat_ioctl_raw_portal input; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ converted.type = input.type; -+ converted.index = input.index; -+ converted.cinh = input.cinh; -+ converted.cena = input.cena; -+ return ioctl_free_raw_portal(fp, ctx, &converted); -+ } -+#endif -+ default: -+ return usdpaa_ioctl(fp, cmd, arg); -+ } -+ return -EINVAL; -+} -+ -+int usdpaa_get_portal_config(struct file *filp, void *cinh, -+ enum usdpaa_portal_type ptype, unsigned int *irq, -+ void **iir_reg) -+{ -+ /* Walk the list of portals for filp and return the config -+ for the portal that matches the hint */ -+ struct ctx *context; -+ struct portal_mapping *portal; -+ -+ /* First sanitize the filp */ -+ if (filp->f_op->open != usdpaa_open) -+ return -ENODEV; -+ context = filp->private_data; -+ spin_lock(&context->lock); -+ list_for_each_entry(portal, &context->portals, list) { -+ if (portal->user.type == ptype && -+ portal->user.addr.cinh == cinh) { -+ if (ptype == usdpaa_portal_qman) { -+ *irq = portal->qportal->public_cfg.irq; -+ *iir_reg = portal->qportal->addr_virt[1] + -+ QM_REG_IIR; -+ } else { -+ *irq = portal->bportal->public_cfg.irq; -+ *iir_reg = portal->bportal->addr_virt[1] + -+ BM_REG_IIR; -+ } -+ spin_unlock(&context->lock); -+ return 0; -+ } -+ } -+ spin_unlock(&context->lock); -+ return -EINVAL; -+} -+ -+static const struct file_operations usdpaa_fops = { -+ .open = usdpaa_open, -+ .release = usdpaa_release, -+ .mmap = usdpaa_mmap, -+ .get_unmapped_area = usdpaa_get_unmapped_area, -+ .unlocked_ioctl = usdpaa_ioctl, -+ .compat_ioctl = usdpaa_ioctl_compat -+}; -+ -+static struct miscdevice usdpaa_miscdev = { -+ .name = "fsl-usdpaa", -+ .fops = &usdpaa_fops, -+ .minor = MISC_DYNAMIC_MINOR, -+}; -+ -+/* Early-boot memory allocation. The boot-arg "usdpaa_mem=<x>" is used to -+ * indicate how much memory (if any) to allocate during early boot. If the -+ * format "usdpaa_mem=<x>,<y>" is used, then <y> will be interpreted as the -+ * number of TLB1 entries to reserve (default is 1). If there are more mappings -+ * than there are TLB1 entries, fault-handling will occur. */ -+ -+static __init int usdpaa_mem(char *arg) -+{ -+ pr_warn("uspdaa_mem argument is depracated\n"); -+ arg_phys_size = memparse(arg, &arg); -+ num_tlb = 1; -+ if (*arg == ',') { -+ unsigned long ul; -+ int err = kstrtoul(arg + 1, 0, &ul); -+ if (err < 0) { -+ num_tlb = 1; -+ pr_warn("ERROR, usdpaa_mem arg is invalid\n"); -+ } else -+ num_tlb = (unsigned int)ul; -+ } -+ return 0; -+} -+early_param("usdpaa_mem", usdpaa_mem); -+ -+static int usdpaa_mem_init(struct reserved_mem *rmem) -+{ -+ phys_start = rmem->base; -+ phys_size = rmem->size; -+ -+ WARN_ON(!(phys_start && phys_size)); -+ -+ return 0; -+} -+RESERVEDMEM_OF_DECLARE(usdpaa_mem_init, "fsl,usdpaa-mem", usdpaa_mem_init); -+ -+__init int fsl_usdpaa_init_early(void) -+{ -+ if (!phys_size || !phys_start) { -+ pr_info("No USDPAA memory, no 'fsl,usdpaa-mem' in device-tree\n"); -+ return 0; -+ } -+ if (phys_size % PAGE_SIZE) { -+ pr_err("'fsl,usdpaa-mem' size must be a multiple of page size\n"); -+ phys_size = 0; -+ return 0; -+ } -+ if (arg_phys_size && phys_size != arg_phys_size) { -+ pr_err("'usdpaa_mem argument size (0x%llx) does not match device tree size (0x%llx)\n", -+ arg_phys_size, phys_size); -+ phys_size = 0; -+ return 0; -+ } -+ pfn_start = phys_start >> PAGE_SHIFT; -+ pfn_size = phys_size >> PAGE_SHIFT; -+#ifdef CONFIG_PPC -+ first_tlb = current_tlb = tlbcam_index; -+ tlbcam_index += num_tlb; -+#endif -+ pr_info("USDPAA region at %llx:%llx(%lx:%lx), %d TLB1 entries)\n", -+ phys_start, phys_size, pfn_start, pfn_size, num_tlb); -+ return 0; -+} -+subsys_initcall(fsl_usdpaa_init_early); -+ -+ -+static int __init usdpaa_init(void) -+{ -+ struct mem_fragment *frag; -+ int ret; -+ u64 tmp_size = phys_size; -+ u64 tmp_start = phys_start; -+ u64 tmp_pfn_size = pfn_size; -+ u64 tmp_pfn_start = pfn_start; -+ -+ pr_info("Freescale USDPAA process driver\n"); -+ if (!phys_start) { -+ pr_warn("fsl-usdpaa: no region found\n"); -+ return 0; -+ } -+ -+ while (tmp_size != 0) { -+ u32 frag_size = largest_page_size(tmp_size); -+ frag = kmalloc(sizeof(*frag), GFP_KERNEL); -+ if (!frag) { -+ pr_err("Failed to setup USDPAA memory accounting\n"); -+ return -ENOMEM; -+ } -+ frag->base = tmp_start; -+ frag->len = frag->root_len = frag_size; -+ frag->root_pfn = tmp_pfn_start; -+ frag->pfn_base = tmp_pfn_start; -+ frag->pfn_len = frag_size / PAGE_SIZE; -+ frag->refs = 0; -+ init_waitqueue_head(&frag->wq); -+ frag->owner = NULL; -+ list_add(&frag->list, &mem_list); -+ -+ /* Adjust for this frag */ -+ tmp_start += frag_size; -+ tmp_size -= frag_size; -+ tmp_pfn_start += frag_size / PAGE_SIZE; -+ tmp_pfn_size -= frag_size / PAGE_SIZE; -+ } -+ ret = misc_register(&usdpaa_miscdev); -+ if (ret) -+ pr_err("fsl-usdpaa: failed to register misc device\n"); -+ return ret; -+} -+ -+static void __exit usdpaa_exit(void) -+{ -+ misc_deregister(&usdpaa_miscdev); -+} -+ -+module_init(usdpaa_init); -+module_exit(usdpaa_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Freescale Semiconductor"); -+MODULE_DESCRIPTION("Freescale USDPAA process driver"); ---- /dev/null -+++ b/drivers/staging/fsl_qbman/fsl_usdpaa_irq.c -@@ -0,0 +1,289 @@ -+/* Copyright (c) 2013 Freescale Semiconductor, Inc. -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+/* define a device that allows USPDAA processes to open a file -+ descriptor and specify which IRQ it wants to montior using an ioctl() -+ When an IRQ is received, the device becomes readable so that a process -+ can use read() or select() type calls to monitor for IRQs */ -+ -+#include <linux/miscdevice.h> -+#include <linux/fs.h> -+#include <linux/cdev.h> -+#include <linux/slab.h> -+#include <linux/interrupt.h> -+#include <linux/poll.h> -+#include <linux/uaccess.h> -+#include <linux/fsl_usdpaa.h> -+#include <linux/module.h> -+#include <linux/fdtable.h> -+#include <linux/file.h> -+ -+#include "qman_low.h" -+#include "bman_low.h" -+ -+struct usdpaa_irq_ctx { -+ int irq_set; /* Set to true once the irq is set via ioctl */ -+ unsigned int irq_num; -+ u32 last_irq_count; /* Last value returned from read */ -+ u32 irq_count; /* Number of irqs since last read */ -+ wait_queue_head_t wait_queue; /* Waiting processes */ -+ spinlock_t lock; -+ void *inhibit_addr; /* inhibit register address */ -+ struct file *usdpaa_filp; -+ char irq_name[128]; -+}; -+ -+static int usdpaa_irq_open(struct inode *inode, struct file *filp) -+{ -+ struct usdpaa_irq_ctx *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; -+ ctx->irq_set = 0; -+ ctx->irq_count = 0; -+ ctx->last_irq_count = 0; -+ init_waitqueue_head(&ctx->wait_queue); -+ spin_lock_init(&ctx->lock); -+ filp->private_data = ctx; -+ return 0; -+} -+ -+static int usdpaa_irq_release(struct inode *inode, struct file *filp) -+{ -+ struct usdpaa_irq_ctx *ctx = filp->private_data; -+ if (ctx->irq_set) { -+ /* Inhibit the IRQ */ -+ out_be32(ctx->inhibit_addr, 0x1); -+ irq_set_affinity_hint(ctx->irq_num, NULL); -+ free_irq(ctx->irq_num, ctx); -+ ctx->irq_set = 0; -+ fput(ctx->usdpaa_filp); -+ } -+ kfree(filp->private_data); -+ return 0; -+} -+ -+static irqreturn_t usdpaa_irq_handler(int irq, void *_ctx) -+{ -+ unsigned long flags; -+ struct usdpaa_irq_ctx *ctx = _ctx; -+ spin_lock_irqsave(&ctx->lock, flags); -+ ++ctx->irq_count; -+ spin_unlock_irqrestore(&ctx->lock, flags); -+ wake_up_all(&ctx->wait_queue); -+ /* Set the inhibit register. This will be reenabled -+ once the USDPAA code handles the IRQ */ -+ out_be32(ctx->inhibit_addr, 0x1); -+ pr_info("Inhibit at %p count %d", ctx->inhibit_addr, ctx->irq_count); -+ return IRQ_HANDLED; -+} -+ -+static int map_irq(struct file *fp, struct usdpaa_ioctl_irq_map *irq_map) -+{ -+ struct usdpaa_irq_ctx *ctx = fp->private_data; -+ int ret; -+ -+ if (ctx->irq_set) { -+ pr_debug("Setting USDPAA IRQ when it was already set!\n"); -+ return -EBUSY; -+ } -+ -+ ctx->usdpaa_filp = fget(irq_map->fd); -+ if (!ctx->usdpaa_filp) { -+ pr_debug("USDPAA fget(%d) returned NULL\n", irq_map->fd); -+ return -EINVAL; -+ } -+ -+ ret = usdpaa_get_portal_config(ctx->usdpaa_filp, irq_map->portal_cinh, -+ irq_map->type, &ctx->irq_num, -+ &ctx->inhibit_addr); -+ if (ret) { -+ pr_debug("USDPAA IRQ couldn't identify portal\n"); -+ fput(ctx->usdpaa_filp); -+ return ret; -+ } -+ -+ ctx->irq_set = 1; -+ -+ snprintf(ctx->irq_name, sizeof(ctx->irq_name), -+ "usdpaa_irq %d", ctx->irq_num); -+ -+ ret = request_irq(ctx->irq_num, usdpaa_irq_handler, 0, -+ ctx->irq_name, ctx); -+ if (ret) { -+ pr_err("USDPAA request_irq(%d) failed, ret= %d\n", -+ ctx->irq_num, ret); -+ ctx->irq_set = 0; -+ fput(ctx->usdpaa_filp); -+ return ret; -+ } -+ ret = irq_set_affinity(ctx->irq_num, ¤t->cpus_allowed); -+ if (ret) -+ pr_err("USDPAA irq_set_affinity() failed, ret= %d\n", ret); -+ -+ ret = irq_set_affinity_hint(ctx->irq_num, ¤t->cpus_allowed); -+ if (ret) -+ pr_err("USDPAA irq_set_affinity_hint() failed, ret= %d\n", ret); -+ -+ return 0; -+} -+ -+static long usdpaa_irq_ioctl(struct file *fp, unsigned int cmd, -+ unsigned long arg) -+{ -+ int ret; -+ struct usdpaa_ioctl_irq_map irq_map; -+ -+ if (cmd != USDPAA_IOCTL_PORTAL_IRQ_MAP) { -+ pr_debug("USDPAA IRQ unknown command 0x%x\n", cmd); -+ return -EINVAL; -+ } -+ -+ ret = copy_from_user(&irq_map, (void __user *)arg, -+ sizeof(irq_map)); -+ if (ret) -+ return ret; -+ return map_irq(fp, &irq_map); -+} -+ -+static ssize_t usdpaa_irq_read(struct file *filp, char __user *buff, -+ size_t count, loff_t *offp) -+{ -+ struct usdpaa_irq_ctx *ctx = filp->private_data; -+ int ret; -+ -+ if (!ctx->irq_set) { -+ pr_debug("Reading USDPAA IRQ before it was set\n"); -+ return -EINVAL; -+ } -+ -+ if (count < sizeof(ctx->irq_count)) { -+ pr_debug("USDPAA IRQ Read too small\n"); -+ return -EINVAL; -+ } -+ if (ctx->irq_count == ctx->last_irq_count) { -+ if (filp->f_flags & O_NONBLOCK) -+ return -EAGAIN; -+ -+ ret = wait_event_interruptible(ctx->wait_queue, -+ ctx->irq_count != ctx->last_irq_count); -+ if (ret == -ERESTARTSYS) -+ return ret; -+ } -+ -+ ctx->last_irq_count = ctx->irq_count; -+ -+ if (copy_to_user(buff, &ctx->last_irq_count, -+ sizeof(ctx->last_irq_count))) -+ return -EFAULT; -+ return sizeof(ctx->irq_count); -+} -+ -+static unsigned int usdpaa_irq_poll(struct file *filp, poll_table *wait) -+{ -+ struct usdpaa_irq_ctx *ctx = filp->private_data; -+ unsigned int ret = 0; -+ unsigned long flags; -+ -+ if (!ctx->irq_set) -+ return POLLHUP; -+ -+ poll_wait(filp, &ctx->wait_queue, wait); -+ -+ spin_lock_irqsave(&ctx->lock, flags); -+ if (ctx->irq_count != ctx->last_irq_count) -+ ret |= POLLIN | POLLRDNORM; -+ spin_unlock_irqrestore(&ctx->lock, flags); -+ return ret; -+} -+ -+static long usdpaa_irq_ioctl_compat(struct file *fp, unsigned int cmd, -+ unsigned long arg) -+{ -+#ifdef CONFIG_COMPAT -+ void __user *a = (void __user *)arg; -+#endif -+ switch (cmd) { -+#ifdef CONFIG_COMPAT -+ case USDPAA_IOCTL_PORTAL_IRQ_MAP_COMPAT: -+ { -+ struct compat_ioctl_irq_map input; -+ struct usdpaa_ioctl_irq_map converted; -+ if (copy_from_user(&input, a, sizeof(input))) -+ return -EFAULT; -+ converted.type = input.type; -+ converted.fd = input.fd; -+ converted.portal_cinh = compat_ptr(input.portal_cinh); -+ return map_irq(fp, &converted); -+ } -+#endif -+ default: -+ return usdpaa_irq_ioctl(fp, cmd, arg); -+ } -+} -+ -+static const struct file_operations usdpaa_irq_fops = { -+ .open = usdpaa_irq_open, -+ .release = usdpaa_irq_release, -+ .unlocked_ioctl = usdpaa_irq_ioctl, -+ .compat_ioctl = usdpaa_irq_ioctl_compat, -+ .read = usdpaa_irq_read, -+ .poll = usdpaa_irq_poll -+}; -+ -+static struct miscdevice usdpaa_miscdev = { -+ .name = "fsl-usdpaa-irq", -+ .fops = &usdpaa_irq_fops, -+ .minor = MISC_DYNAMIC_MINOR, -+}; -+ -+static int __init usdpaa_irq_init(void) -+{ -+ int ret; -+ -+ pr_info("Freescale USDPAA process IRQ driver\n"); -+ ret = misc_register(&usdpaa_miscdev); -+ if (ret) -+ pr_err("fsl-usdpaa-irq: failed to register misc device\n"); -+ return ret; -+} -+ -+static void __exit usdpaa_irq_exit(void) -+{ -+ misc_deregister(&usdpaa_miscdev); -+} -+ -+module_init(usdpaa_irq_init); -+module_exit(usdpaa_irq_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Freescale Semiconductor"); -+MODULE_DESCRIPTION("Freescale USDPAA process IRQ driver"); ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qbman_driver.c -@@ -0,0 +1,88 @@ -+/* Copyright 2013 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include <linux/time.h> -+#include "qman_private.h" -+#include "bman_private.h" -+__init void qman_init_early(void); -+__init void bman_init_early(void); -+ -+static __init int qbman_init(void) -+{ -+ struct device_node *dn; -+ u32 is_portal_available; -+ -+ bman_init(); -+ qman_init(); -+ -+ is_portal_available = 0; -+ for_each_compatible_node(dn, NULL, "fsl,qman-portal") { -+ if (!of_device_is_available(dn)) -+ continue; -+ else -+ is_portal_available = 1; -+ } -+ -+ if (!qman_have_ccsr() && is_portal_available) { -+ struct qman_fq fq = { -+ .fqid = 1 -+ }; -+ struct qm_mcr_queryfq_np np; -+ int err, retry = CONFIG_FSL_QMAN_INIT_TIMEOUT; -+ struct timespec nowts, diffts, startts = current_kernel_time(); -+ /* Loop while querying given fqid succeeds or time out */ -+ while (1) { -+ err = qman_query_fq_np(&fq, &np); -+ if (!err) { -+ /* success, control-plane has configured QMan */ -+ break; -+ } else if (err != -ERANGE) { -+ pr_err("QMan: I/O error, continuing anyway\n"); -+ break; -+ } -+ nowts = current_kernel_time(); -+ diffts = timespec_sub(nowts, startts); -+ if (diffts.tv_sec > 0) { -+ if (!retry--) { -+ pr_err("QMan: time out, control-plane" -+ " dead?\n"); -+ break; -+ } -+ pr_warn("QMan: polling for the control-plane" -+ " (%d)\n", retry); -+ } -+ } -+ } -+ bman_resource_init(); -+ qman_resource_init(); -+ return 0; -+} -+subsys_initcall(qbman_init); ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_config.c -@@ -0,0 +1,1224 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include <asm/cacheflush.h> -+#include "qman_private.h" -+#include <linux/highmem.h> -+#include <linux/of_reserved_mem.h> -+ -+/* Last updated for v00.800 of the BG */ -+ -+/* Register offsets */ -+#define REG_QCSP_LIO_CFG(n) (0x0000 + ((n) * 0x10)) -+#define REG_QCSP_IO_CFG(n) (0x0004 + ((n) * 0x10)) -+#define REG_QCSP_DD_CFG(n) (0x000c + ((n) * 0x10)) -+#define REG_DD_CFG 0x0200 -+#define REG_DCP_CFG(n) (0x0300 + ((n) * 0x10)) -+#define REG_DCP_DD_CFG(n) (0x0304 + ((n) * 0x10)) -+#define REG_DCP_DLM_AVG(n) (0x030c + ((n) * 0x10)) -+#define REG_PFDR_FPC 0x0400 -+#define REG_PFDR_FP_HEAD 0x0404 -+#define REG_PFDR_FP_TAIL 0x0408 -+#define REG_PFDR_FP_LWIT 0x0410 -+#define REG_PFDR_CFG 0x0414 -+#define REG_SFDR_CFG 0x0500 -+#define REG_SFDR_IN_USE 0x0504 -+#define REG_WQ_CS_CFG(n) (0x0600 + ((n) * 0x04)) -+#define REG_WQ_DEF_ENC_WQID 0x0630 -+#define REG_WQ_SC_DD_CFG(n) (0x640 + ((n) * 0x04)) -+#define REG_WQ_PC_DD_CFG(n) (0x680 + ((n) * 0x04)) -+#define REG_WQ_DC0_DD_CFG(n) (0x6c0 + ((n) * 0x04)) -+#define REG_WQ_DC1_DD_CFG(n) (0x700 + ((n) * 0x04)) -+#define REG_WQ_DCn_DD_CFG(n) (0x6c0 + ((n) * 0x40)) /* n=2,3 */ -+#define REG_CM_CFG 0x0800 -+#define REG_ECSR 0x0a00 -+#define REG_ECIR 0x0a04 -+#define REG_EADR 0x0a08 -+#define REG_ECIR2 0x0a0c -+#define REG_EDATA(n) (0x0a10 + ((n) * 0x04)) -+#define REG_SBEC(n) (0x0a80 + ((n) * 0x04)) -+#define REG_MCR 0x0b00 -+#define REG_MCP(n) (0x0b04 + ((n) * 0x04)) -+#define REG_MISC_CFG 0x0be0 -+#define REG_HID_CFG 0x0bf0 -+#define REG_IDLE_STAT 0x0bf4 -+#define REG_IP_REV_1 0x0bf8 -+#define REG_IP_REV_2 0x0bfc -+#define REG_FQD_BARE 0x0c00 -+#define REG_PFDR_BARE 0x0c20 -+#define REG_offset_BAR 0x0004 /* relative to REG_[FQD|PFDR]_BARE */ -+#define REG_offset_AR 0x0010 /* relative to REG_[FQD|PFDR]_BARE */ -+#define REG_QCSP_BARE 0x0c80 -+#define REG_QCSP_BAR 0x0c84 -+#define REG_CI_SCHED_CFG 0x0d00 -+#define REG_SRCIDR 0x0d04 -+#define REG_LIODNR 0x0d08 -+#define REG_CI_RLM_AVG 0x0d14 -+#define REG_ERR_ISR 0x0e00 /* + "enum qm_isr_reg" */ -+#define REG_REV3_QCSP_LIO_CFG(n) (0x1000 + ((n) * 0x10)) -+#define REG_REV3_QCSP_IO_CFG(n) (0x1004 + ((n) * 0x10)) -+#define REG_REV3_QCSP_DD_CFG(n) (0x100c + ((n) * 0x10)) -+#define REG_CEETM_CFG_IDX 0x900 -+#define REG_CEETM_CFG_PRES 0x904 -+#define REG_CEETM_XSFDR_IN_USE 0x908 -+ -+/* Assists for QMAN_MCR */ -+#define MCR_INIT_PFDR 0x01000000 -+#define MCR_get_rslt(v) (u8)((v) >> 24) -+#define MCR_rslt_idle(r) (!rslt || (rslt >= 0xf0)) -+#define MCR_rslt_ok(r) (rslt == 0xf0) -+#define MCR_rslt_eaccess(r) (rslt == 0xf8) -+#define MCR_rslt_inval(r) (rslt == 0xff) -+ -+struct qman; -+ -+/* Follows WQ_CS_CFG0-5 */ -+enum qm_wq_class { -+ qm_wq_portal = 0, -+ qm_wq_pool = 1, -+ qm_wq_fman0 = 2, -+ qm_wq_fman1 = 3, -+ qm_wq_caam = 4, -+ qm_wq_pme = 5, -+ qm_wq_first = qm_wq_portal, -+ qm_wq_last = qm_wq_pme -+}; -+ -+/* Follows FQD_[BARE|BAR|AR] and PFDR_[BARE|BAR|AR] */ -+enum qm_memory { -+ qm_memory_fqd, -+ qm_memory_pfdr -+}; -+ -+/* Used by all error interrupt registers except 'inhibit' */ -+#define QM_EIRQ_CIDE 0x20000000 /* Corenet Initiator Data Error */ -+#define QM_EIRQ_CTDE 0x10000000 /* Corenet Target Data Error */ -+#define QM_EIRQ_CITT 0x08000000 /* Corenet Invalid Target Transaction */ -+#define QM_EIRQ_PLWI 0x04000000 /* PFDR Low Watermark */ -+#define QM_EIRQ_MBEI 0x02000000 /* Multi-bit ECC Error */ -+#define QM_EIRQ_SBEI 0x01000000 /* Single-bit ECC Error */ -+#define QM_EIRQ_PEBI 0x00800000 /* PFDR Enqueues Blocked Interrupt */ -+#define QM_EIRQ_IFSI 0x00020000 /* Invalid FQ Flow Control State */ -+#define QM_EIRQ_ICVI 0x00010000 /* Invalid Command Verb */ -+#define QM_EIRQ_IDDI 0x00000800 /* Invalid Dequeue (Direct-connect) */ -+#define QM_EIRQ_IDFI 0x00000400 /* Invalid Dequeue FQ */ -+#define QM_EIRQ_IDSI 0x00000200 /* Invalid Dequeue Source */ -+#define QM_EIRQ_IDQI 0x00000100 /* Invalid Dequeue Queue */ -+#define QM_EIRQ_IECE 0x00000010 /* Invalid Enqueue Configuration */ -+#define QM_EIRQ_IEOI 0x00000008 /* Invalid Enqueue Overflow */ -+#define QM_EIRQ_IESI 0x00000004 /* Invalid Enqueue State */ -+#define QM_EIRQ_IECI 0x00000002 /* Invalid Enqueue Channel */ -+#define QM_EIRQ_IEQI 0x00000001 /* Invalid Enqueue Queue */ -+ -+/* QMAN_ECIR valid error bit */ -+#define PORTAL_ECSR_ERR (QM_EIRQ_IEQI | QM_EIRQ_IESI | QM_EIRQ_IEOI | \ -+ QM_EIRQ_IDQI | QM_EIRQ_IDSI | QM_EIRQ_IDFI | \ -+ QM_EIRQ_IDDI | QM_EIRQ_ICVI | QM_EIRQ_IFSI) -+#define FQID_ECSR_ERR (QM_EIRQ_IEQI | QM_EIRQ_IECI | QM_EIRQ_IESI | \ -+ QM_EIRQ_IEOI | QM_EIRQ_IDQI | QM_EIRQ_IDFI | \ -+ QM_EIRQ_IFSI) -+ -+union qman_ecir { -+ u32 ecir_raw; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 __reserved:2; -+ u32 portal_type:1; -+ u32 portal_num:5; -+ u32 fqid:24; -+#else -+ u32 fqid:24; -+ u32 portal_num:5; -+ u32 portal_type:1; -+ u32 __reserved:2; -+#endif -+ } __packed info; -+}; -+ -+union qman_ecir2 { -+ u32 ecir2_raw; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 portal_type:1; -+ u32 __reserved:21; -+ u32 portal_num:10; -+#else -+ u32 portal_num:10; -+ u32 __reserved:21; -+ u32 portal_type:1; -+#endif -+ } __packed info; -+}; -+ -+union qman_eadr { -+ u32 eadr_raw; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 __reserved1:4; -+ u32 memid:4; -+ u32 __reserved2:12; -+ u32 eadr:12; -+#else -+ u32 eadr:12; -+ u32 __reserved2:12; -+ u32 memid:4; -+ u32 __reserved1:4; -+#endif -+ } __packed info; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 __reserved1:3; -+ u32 memid:5; -+ u32 __reserved:8; -+ u32 eadr:16; -+#else -+ u32 eadr:16; -+ u32 __reserved:8; -+ u32 memid:5; -+ u32 __reserved1:3; -+#endif -+ } __packed info_rev3; -+}; -+ -+struct qman_hwerr_txt { -+ u32 mask; -+ const char *txt; -+}; -+ -+#define QMAN_HWE_TXT(a, b) { .mask = QM_EIRQ_##a, .txt = b } -+ -+static const struct qman_hwerr_txt qman_hwerr_txts[] = { -+ QMAN_HWE_TXT(CIDE, "Corenet Initiator Data Error"), -+ QMAN_HWE_TXT(CTDE, "Corenet Target Data Error"), -+ QMAN_HWE_TXT(CITT, "Corenet Invalid Target Transaction"), -+ QMAN_HWE_TXT(PLWI, "PFDR Low Watermark"), -+ QMAN_HWE_TXT(MBEI, "Multi-bit ECC Error"), -+ QMAN_HWE_TXT(SBEI, "Single-bit ECC Error"), -+ QMAN_HWE_TXT(PEBI, "PFDR Enqueues Blocked Interrupt"), -+ QMAN_HWE_TXT(ICVI, "Invalid Command Verb"), -+ QMAN_HWE_TXT(IFSI, "Invalid Flow Control State"), -+ QMAN_HWE_TXT(IDDI, "Invalid Dequeue (Direct-connect)"), -+ QMAN_HWE_TXT(IDFI, "Invalid Dequeue FQ"), -+ QMAN_HWE_TXT(IDSI, "Invalid Dequeue Source"), -+ QMAN_HWE_TXT(IDQI, "Invalid Dequeue Queue"), -+ QMAN_HWE_TXT(IECE, "Invalid Enqueue Configuration"), -+ QMAN_HWE_TXT(IEOI, "Invalid Enqueue Overflow"), -+ QMAN_HWE_TXT(IESI, "Invalid Enqueue State"), -+ QMAN_HWE_TXT(IECI, "Invalid Enqueue Channel"), -+ QMAN_HWE_TXT(IEQI, "Invalid Enqueue Queue") -+}; -+#define QMAN_HWE_COUNT (sizeof(qman_hwerr_txts)/sizeof(struct qman_hwerr_txt)) -+ -+struct qman_error_info_mdata { -+ u16 addr_mask; -+ u16 bits; -+ const char *txt; -+}; -+ -+#define QMAN_ERR_MDATA(a, b, c) { .addr_mask = a, .bits = b, .txt = c} -+static const struct qman_error_info_mdata error_mdata[] = { -+ QMAN_ERR_MDATA(0x01FF, 24, "FQD cache tag memory 0"), -+ QMAN_ERR_MDATA(0x01FF, 24, "FQD cache tag memory 1"), -+ QMAN_ERR_MDATA(0x01FF, 24, "FQD cache tag memory 2"), -+ QMAN_ERR_MDATA(0x01FF, 24, "FQD cache tag memory 3"), -+ QMAN_ERR_MDATA(0x0FFF, 512, "FQD cache memory"), -+ QMAN_ERR_MDATA(0x07FF, 128, "SFDR memory"), -+ QMAN_ERR_MDATA(0x01FF, 72, "WQ context memory"), -+ QMAN_ERR_MDATA(0x00FF, 240, "CGR memory"), -+ QMAN_ERR_MDATA(0x00FF, 302, "Internal Order Restoration List memory"), -+ QMAN_ERR_MDATA(0x01FF, 256, "SW portal ring memory"), -+ QMAN_ERR_MDATA(0x07FF, 181, "CEETM class queue descriptor memory"), -+ QMAN_ERR_MDATA(0x0FFF, 140, "CEETM extended SFDR memory"), -+ QMAN_ERR_MDATA(0x0FFF, 25, "CEETM logical FQ mapping memory"), -+ QMAN_ERR_MDATA(0x0FFF, 96, "CEETM dequeue context memory"), -+ QMAN_ERR_MDATA(0x07FF, 396, "CEETM ccgr memory"), -+ QMAN_ERR_MDATA(0x00FF, 146, "CEETM CQ channel shaping memory"), -+ QMAN_ERR_MDATA(0x007F, 256, "CEETM CQ channel scheduling memory"), -+ QMAN_ERR_MDATA(0x01FF, 88, "CEETM dequeue statistics memory"), -+}; -+#define QMAN_ERR_MDATA_COUNT \ -+ (sizeof(error_mdata)/sizeof(struct qman_error_info_mdata)) -+ -+/* Add this in Kconfig */ -+#define QMAN_ERRS_TO_UNENABLE (QM_EIRQ_PLWI | QM_EIRQ_PEBI) -+ -+/** -+ * qm_err_isr_<reg>_<verb> - Manipulate global interrupt registers -+ * @v: for accessors that write values, this is the 32-bit value -+ * -+ * Manipulates QMAN_ERR_ISR, QMAN_ERR_IER, QMAN_ERR_ISDR, QMAN_ERR_IIR. All -+ * manipulations except qm_err_isr_[un]inhibit() use 32-bit masks composed of -+ * the QM_EIRQ_*** definitions. Note that "qm_err_isr_enable_write" means -+ * "write the enable register" rather than "enable the write register"! -+ */ -+#define qm_err_isr_status_read(qm) \ -+ __qm_err_isr_read(qm, qm_isr_status) -+#define qm_err_isr_status_clear(qm, m) \ -+ __qm_err_isr_write(qm, qm_isr_status, m) -+#define qm_err_isr_enable_read(qm) \ -+ __qm_err_isr_read(qm, qm_isr_enable) -+#define qm_err_isr_enable_write(qm, v) \ -+ __qm_err_isr_write(qm, qm_isr_enable, v) -+#define qm_err_isr_disable_read(qm) \ -+ __qm_err_isr_read(qm, qm_isr_disable) -+#define qm_err_isr_disable_write(qm, v) \ -+ __qm_err_isr_write(qm, qm_isr_disable, v) -+#define qm_err_isr_inhibit(qm) \ -+ __qm_err_isr_write(qm, qm_isr_inhibit, 1) -+#define qm_err_isr_uninhibit(qm) \ -+ __qm_err_isr_write(qm, qm_isr_inhibit, 0) -+ -+/* -+ * TODO: unimplemented registers -+ * -+ * Keeping a list here of Qman registers I have not yet covered; -+ * QCSP_DD_IHRSR, QCSP_DD_IHRFR, QCSP_DD_HASR, -+ * DCP_DD_IHRSR, DCP_DD_IHRFR, DCP_DD_HASR, CM_CFG, -+ * QMAN_EECC, QMAN_SBET, QMAN_EINJ, QMAN_SBEC0-12 -+ */ -+ -+/* Encapsulate "struct qman *" as a cast of the register space address. */ -+ -+static struct qman *qm_create(void *regs) -+{ -+ return (struct qman *)regs; -+} -+ -+static inline u32 __qm_in(struct qman *qm, u32 offset) -+{ -+ return in_be32((void *)qm + offset); -+} -+static inline void __qm_out(struct qman *qm, u32 offset, u32 val) -+{ -+ out_be32((void *)qm + offset, val); -+} -+#define qm_in(reg) __qm_in(qm, REG_##reg) -+#define qm_out(reg, val) __qm_out(qm, REG_##reg, val) -+ -+static u32 __qm_err_isr_read(struct qman *qm, enum qm_isr_reg n) -+{ -+ return __qm_in(qm, REG_ERR_ISR + (n << 2)); -+} -+ -+static void __qm_err_isr_write(struct qman *qm, enum qm_isr_reg n, u32 val) -+{ -+ __qm_out(qm, REG_ERR_ISR + (n << 2), val); -+} -+ -+static void qm_set_dc(struct qman *qm, enum qm_dc_portal portal, -+ int ed, u8 sernd) -+{ -+ DPA_ASSERT(!ed || (portal == qm_dc_portal_fman0) || -+ (portal == qm_dc_portal_fman1)); -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) -+ qm_out(DCP_CFG(portal), (ed ? 0x1000 : 0) | (sernd & 0x3ff)); -+ else -+ qm_out(DCP_CFG(portal), (ed ? 0x100 : 0) | (sernd & 0x1f)); -+} -+ -+static void qm_set_wq_scheduling(struct qman *qm, enum qm_wq_class wq_class, -+ u8 cs_elev, u8 csw2, u8 csw3, u8 csw4, u8 csw5, -+ u8 csw6, u8 csw7) -+{ -+ qm_out(WQ_CS_CFG(wq_class), ((cs_elev & 0xff) << 24) | -+ ((csw2 & 0x7) << 20) | ((csw3 & 0x7) << 16) | -+ ((csw4 & 0x7) << 12) | ((csw5 & 0x7) << 8) | -+ ((csw6 & 0x7) << 4) | (csw7 & 0x7)); -+} -+ -+static void qm_set_hid(struct qman *qm) -+{ -+ qm_out(HID_CFG, 0); -+} -+ -+static void qm_set_corenet_initiator(struct qman *qm) -+{ -+ qm_out(CI_SCHED_CFG, -+ 0x80000000 | /* write srcciv enable */ -+ (CONFIG_FSL_QMAN_CI_SCHED_CFG_SRCCIV << 24) | -+ (CONFIG_FSL_QMAN_CI_SCHED_CFG_SRQ_W << 8) | -+ (CONFIG_FSL_QMAN_CI_SCHED_CFG_RW_W << 4) | -+ CONFIG_FSL_QMAN_CI_SCHED_CFG_BMAN_W); -+} -+ -+static void qm_get_version(struct qman *qm, u16 *id, u8 *major, u8 *minor, -+ u8 *cfg) -+{ -+ u32 v = qm_in(IP_REV_1); -+ u32 v2 = qm_in(IP_REV_2); -+ *id = (v >> 16); -+ *major = (v >> 8) & 0xff; -+ *minor = v & 0xff; -+ *cfg = v2 & 0xff; -+} -+ -+static void qm_set_memory(struct qman *qm, enum qm_memory memory, u64 ba, -+ int enable, int prio, int stash, u32 size) -+{ -+ u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE; -+ u32 exp = ilog2(size); -+ /* choke if size isn't within range */ -+ DPA_ASSERT((size >= 4096) && (size <= 1073741824) && -+ is_power_of_2(size)); -+ /* choke if 'ba' has lower-alignment than 'size' */ -+ DPA_ASSERT(!(ba & (size - 1))); -+ __qm_out(qm, offset, upper_32_bits(ba)); -+ __qm_out(qm, offset + REG_offset_BAR, lower_32_bits(ba)); -+ __qm_out(qm, offset + REG_offset_AR, -+ (enable ? 0x80000000 : 0) | -+ (prio ? 0x40000000 : 0) | -+ (stash ? 0x20000000 : 0) | -+ (exp - 1)); -+} -+ -+static void qm_set_pfdr_threshold(struct qman *qm, u32 th, u8 k) -+{ -+ qm_out(PFDR_FP_LWIT, th & 0xffffff); -+ qm_out(PFDR_CFG, k); -+} -+ -+static void qm_set_sfdr_threshold(struct qman *qm, u16 th) -+{ -+ qm_out(SFDR_CFG, th & 0x3ff); -+} -+ -+static int qm_init_pfdr(struct qman *qm, u32 pfdr_start, u32 num) -+{ -+ u8 rslt = MCR_get_rslt(qm_in(MCR)); -+ -+ DPA_ASSERT(pfdr_start && !(pfdr_start & 7) && !(num & 7) && num); -+ /* Make sure the command interface is 'idle' */ -+ if (!MCR_rslt_idle(rslt)) -+ panic("QMAN_MCR isn't idle"); -+ -+ /* Write the MCR command params then the verb */ -+ qm_out(MCP(0), pfdr_start); -+ /* TODO: remove this - it's a workaround for a model bug that is -+ * corrected in more recent versions. We use the workaround until -+ * everyone has upgraded. */ -+ qm_out(MCP(1), (pfdr_start + num - 16)); -+ lwsync(); -+ qm_out(MCR, MCR_INIT_PFDR); -+ /* Poll for the result */ -+ do { -+ rslt = MCR_get_rslt(qm_in(MCR)); -+ } while (!MCR_rslt_idle(rslt)); -+ if (MCR_rslt_ok(rslt)) -+ return 0; -+ if (MCR_rslt_eaccess(rslt)) -+ return -EACCES; -+ if (MCR_rslt_inval(rslt)) -+ return -EINVAL; -+ pr_crit("Unexpected result from MCR_INIT_PFDR: %02x\n", rslt); -+ return -ENOSYS; -+} -+ -+/*****************/ -+/* Config driver */ -+/*****************/ -+ -+#define DEFAULT_FQD_SZ (PAGE_SIZE << CONFIG_FSL_QMAN_FQD_SZ) -+#define DEFAULT_PFDR_SZ (PAGE_SIZE << CONFIG_FSL_QMAN_PFDR_SZ) -+ -+/* We support only one of these */ -+static struct qman *qm; -+static struct device_node *qm_node; -+ -+/* And this state belongs to 'qm'. It is set during fsl_qman_init(), but used -+ * during qman_init_ccsr(). */ -+static dma_addr_t fqd_a, pfdr_a; -+static size_t fqd_sz = DEFAULT_FQD_SZ, pfdr_sz = DEFAULT_PFDR_SZ; -+ -+static int qman_fqd(struct reserved_mem *rmem) -+{ -+ fqd_a = rmem->base; -+ fqd_sz = rmem->size; -+ -+ WARN_ON(!(fqd_a && fqd_sz)); -+ -+ return 0; -+} -+RESERVEDMEM_OF_DECLARE(qman_fqd, "fsl,qman-fqd", qman_fqd); -+ -+static int qman_pfdr(struct reserved_mem *rmem) -+{ -+ pfdr_a = rmem->base; -+ pfdr_sz = rmem->size; -+ -+ WARN_ON(!(pfdr_a && pfdr_sz)); -+ -+ return 0; -+} -+RESERVEDMEM_OF_DECLARE(qman_fbpr, "fsl,qman-pfdr", qman_pfdr); -+ -+size_t get_qman_fqd_size() -+{ -+ return fqd_sz; -+} -+ -+/* Parse the <name> property to extract the memory location and size and -+ * memblock_reserve() it. If it isn't supplied, memblock_alloc() the default -+ * size. Also flush this memory range from data cache so that QMAN originated -+ * transactions for this memory region could be marked non-coherent. -+ */ -+static __init int parse_mem_property(struct device_node *node, const char *name, -+ dma_addr_t *addr, size_t *sz, int zero) -+{ -+ int ret; -+ -+ /* If using a "zero-pma", don't try to zero it, even if you asked */ -+ if (zero && of_find_property(node, "zero-pma", &ret)) { -+ pr_info(" it's a 'zero-pma', not zeroing from s/w\n"); -+ zero = 0; -+ } -+ -+ if (zero) { -+ /* map as cacheable, non-guarded */ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ void __iomem *tmpp = ioremap_cache(*addr, *sz); -+#else -+ void __iomem *tmpp = ioremap(*addr, *sz); -+#endif -+ -+ if (!tmpp) -+ return -ENOMEM; -+ memset_io(tmpp, 0, *sz); -+ flush_dcache_range((unsigned long)tmpp, -+ (unsigned long)tmpp + *sz); -+ iounmap(tmpp); -+ } -+ -+ return 0; -+} -+ -+/* TODO: -+ * - there is obviously no handling of errors, -+ * - the calls to qm_set_memory() hard-code the priority and CPC-stashing for -+ * both memory resources to zero. -+ */ -+static int __init fsl_qman_init(struct device_node *node) -+{ -+ struct resource res; -+ resource_size_t len; -+ u32 __iomem *regs; -+ const char *s; -+ int ret, standby = 0; -+ u16 id; -+ u8 major, minor, cfg; -+ ret = of_address_to_resource(node, 0, &res); -+ if (ret) { -+ pr_err("Can't get %s property '%s'\n", node->full_name, "reg"); -+ return ret; -+ } -+ s = of_get_property(node, "fsl,hv-claimable", &ret); -+ if (s && !strcmp(s, "standby")) -+ standby = 1; -+ if (!standby) { -+ ret = parse_mem_property(node, "fsl,qman-fqd", -+ &fqd_a, &fqd_sz, 1); -+ pr_info("qman-fqd addr %pad size 0x%zx\n", &fqd_a, fqd_sz); -+ BUG_ON(ret); -+ ret = parse_mem_property(node, "fsl,qman-pfdr", -+ &pfdr_a, &pfdr_sz, 0); -+ pr_info("qman-pfdr addr %pad size 0x%zx\n", &pfdr_a, pfdr_sz); -+ BUG_ON(ret); -+ } -+ /* Global configuration */ -+ len = resource_size(&res); -+ if (len != (unsigned long)len) -+ return -EINVAL; -+ regs = ioremap(res.start, (unsigned long)len); -+ qm = qm_create(regs); -+ qm_node = node; -+ qm_get_version(qm, &id, &major, &minor, &cfg); -+ pr_info("Qman ver:%04x,%02x,%02x,%02x\n", id, major, minor, cfg); -+ if (!qman_ip_rev) { -+ if ((major == 1) && (minor == 0)) { -+ pr_err("QMAN rev1.0 on P4080 rev1 is not supported!\n"); -+ iounmap(regs); -+ return -ENODEV; -+ } else if ((major == 1) && (minor == 1)) -+ qman_ip_rev = QMAN_REV11; -+ else if ((major == 1) && (minor == 2)) -+ qman_ip_rev = QMAN_REV12; -+ else if ((major == 2) && (minor == 0)) -+ qman_ip_rev = QMAN_REV20; -+ else if ((major == 3) && (minor == 0)) -+ qman_ip_rev = QMAN_REV30; -+ else if ((major == 3) && (minor == 1)) -+ qman_ip_rev = QMAN_REV31; -+ else if ((major == 3) && (minor == 2)) -+ qman_ip_rev = QMAN_REV32; -+ else { -+ pr_warn("unknown Qman version, default to rev1.1\n"); -+ qman_ip_rev = QMAN_REV11; -+ } -+ qman_ip_cfg = cfg; -+ } -+ -+ if (standby) { -+ pr_info(" -> in standby mode\n"); -+ return 0; -+ } -+ return 0; -+} -+ -+int qman_have_ccsr(void) -+{ -+ return qm ? 1 : 0; -+} -+ -+__init int qman_init_early(void) -+{ -+ struct device_node *dn; -+ int ret; -+ -+ for_each_compatible_node(dn, NULL, "fsl,qman") { -+ if (qm) -+ pr_err("%s: only one 'fsl,qman' allowed\n", -+ dn->full_name); -+ else { -+ if (!of_device_is_available(dn)) -+ continue; -+ -+ ret = fsl_qman_init(dn); -+ BUG_ON(ret); -+ } -+ } -+ return 0; -+} -+postcore_initcall_sync(qman_init_early); -+ -+static void log_edata_bits(u32 bit_count) -+{ -+ u32 i, j, mask = 0xffffffff; -+ -+ pr_warn("Qman ErrInt, EDATA:\n"); -+ i = bit_count/32; -+ if (bit_count%32) { -+ i++; -+ mask = ~(mask << bit_count%32); -+ } -+ j = 16-i; -+ pr_warn(" 0x%08x\n", qm_in(EDATA(j)) & mask); -+ j++; -+ for (; j < 16; j++) -+ pr_warn(" 0x%08x\n", qm_in(EDATA(j))); -+} -+ -+static void log_additional_error_info(u32 isr_val, u32 ecsr_val) -+{ -+ union qman_ecir ecir_val; -+ union qman_eadr eadr_val; -+ -+ ecir_val.ecir_raw = qm_in(ECIR); -+ /* Is portal info valid */ -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) { -+ union qman_ecir2 ecir2_val; -+ ecir2_val.ecir2_raw = qm_in(ECIR2); -+ if (ecsr_val & PORTAL_ECSR_ERR) { -+ pr_warn("Qman ErrInt: %s id %d\n", -+ (ecir2_val.info.portal_type) ? -+ "DCP" : "SWP", ecir2_val.info.portal_num); -+ } -+ if (ecsr_val & (FQID_ECSR_ERR | QM_EIRQ_IECE)) { -+ pr_warn("Qman ErrInt: ecir.fqid 0x%x\n", -+ ecir_val.info.fqid); -+ } -+ if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) { -+ eadr_val.eadr_raw = qm_in(EADR); -+ pr_warn("Qman ErrInt: EADR Memory: %s, 0x%x\n", -+ error_mdata[eadr_val.info_rev3.memid].txt, -+ error_mdata[eadr_val.info_rev3.memid].addr_mask -+ & eadr_val.info_rev3.eadr); -+ log_edata_bits( -+ error_mdata[eadr_val.info_rev3.memid].bits); -+ } -+ } else { -+ if (ecsr_val & PORTAL_ECSR_ERR) { -+ pr_warn("Qman ErrInt: %s id %d\n", -+ (ecir_val.info.portal_type) ? -+ "DCP" : "SWP", ecir_val.info.portal_num); -+ } -+ if (ecsr_val & FQID_ECSR_ERR) { -+ pr_warn("Qman ErrInt: ecir.fqid 0x%x\n", -+ ecir_val.info.fqid); -+ } -+ if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) { -+ eadr_val.eadr_raw = qm_in(EADR); -+ pr_warn("Qman ErrInt: EADR Memory: %s, 0x%x\n", -+ error_mdata[eadr_val.info.memid].txt, -+ error_mdata[eadr_val.info.memid].addr_mask -+ & eadr_val.info.eadr); -+ log_edata_bits(error_mdata[eadr_val.info.memid].bits); -+ } -+ } -+} -+ -+/* Qman interrupt handler */ -+static irqreturn_t qman_isr(int irq, void *ptr) -+{ -+ u32 isr_val, ier_val, ecsr_val, isr_mask, i; -+ -+ ier_val = qm_err_isr_enable_read(qm); -+ isr_val = qm_err_isr_status_read(qm); -+ ecsr_val = qm_in(ECSR); -+ isr_mask = isr_val & ier_val; -+ -+ if (!isr_mask) -+ return IRQ_NONE; -+ for (i = 0; i < QMAN_HWE_COUNT; i++) { -+ if (qman_hwerr_txts[i].mask & isr_mask) { -+ pr_warn("Qman ErrInt: %s\n", qman_hwerr_txts[i].txt); -+ if (qman_hwerr_txts[i].mask & ecsr_val) { -+ log_additional_error_info(isr_mask, ecsr_val); -+ /* Re-arm error capture registers */ -+ qm_out(ECSR, ecsr_val); -+ } -+ if (qman_hwerr_txts[i].mask & QMAN_ERRS_TO_UNENABLE) { -+ pr_devel("Qman un-enabling error 0x%x\n", -+ qman_hwerr_txts[i].mask); -+ ier_val &= ~qman_hwerr_txts[i].mask; -+ qm_err_isr_enable_write(qm, ier_val); -+ } -+ } -+ } -+ qm_err_isr_status_clear(qm, isr_val); -+ return IRQ_HANDLED; -+} -+ -+static int __bind_irq(void) -+{ -+ int ret, err_irq; -+ -+ err_irq = of_irq_to_resource(qm_node, 0, NULL); -+ if (err_irq == 0) { -+ pr_info("Can't get %s property '%s'\n", qm_node->full_name, -+ "interrupts"); -+ return -ENODEV; -+ } -+ ret = request_irq(err_irq, qman_isr, IRQF_SHARED, "qman-err", qm_node); -+ if (ret) { -+ pr_err("request_irq() failed %d for '%s'\n", ret, -+ qm_node->full_name); -+ return -ENODEV; -+ } -+ /* Write-to-clear any stale bits, (eg. starvation being asserted prior -+ * to resource allocation during driver init). */ -+ qm_err_isr_status_clear(qm, 0xffffffff); -+ /* Enable Error Interrupts */ -+ qm_err_isr_enable_write(qm, 0xffffffff); -+ return 0; -+} -+ -+int qman_init_ccsr(struct device_node *node) -+{ -+ int ret; -+ if (!qman_have_ccsr()) -+ return 0; -+ if (node != qm_node) -+ return -EINVAL; -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ /* TEMP for LS1043 : should be done in uboot */ -+ qm_out(QCSP_BARE, 0x5); -+ qm_out(QCSP_BAR, 0x0); -+#endif -+ /* FQD memory */ -+ qm_set_memory(qm, qm_memory_fqd, fqd_a, 1, 0, 0, fqd_sz); -+ /* PFDR memory */ -+ qm_set_memory(qm, qm_memory_pfdr, pfdr_a, 1, 0, 0, pfdr_sz); -+ qm_init_pfdr(qm, 8, pfdr_sz / 64 - 8); -+ /* thresholds */ -+ qm_set_pfdr_threshold(qm, 512, 64); -+ qm_set_sfdr_threshold(qm, 128); -+ /* clear stale PEBI bit from interrupt status register */ -+ qm_err_isr_status_clear(qm, QM_EIRQ_PEBI); -+ /* corenet initiator settings */ -+ qm_set_corenet_initiator(qm); -+ /* HID settings */ -+ qm_set_hid(qm); -+ /* Set scheduling weights to defaults */ -+ for (ret = qm_wq_first; ret <= qm_wq_last; ret++) -+ qm_set_wq_scheduling(qm, ret, 0, 0, 0, 0, 0, 0, 0); -+ /* We are not prepared to accept ERNs for hardware enqueues */ -+ qm_set_dc(qm, qm_dc_portal_fman0, 1, 0); -+ qm_set_dc(qm, qm_dc_portal_fman1, 1, 0); -+ /* Initialise Error Interrupt Handler */ -+ ret = __bind_irq(); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+#define LIO_CFG_LIODN_MASK 0x0fff0000 -+void qman_liodn_fixup(u16 channel) -+{ -+ static int done; -+ static u32 liodn_offset; -+ u32 before, after; -+ int idx = channel - QM_CHANNEL_SWPORTAL0; -+ -+ if (!qman_have_ccsr()) -+ return; -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) -+ before = qm_in(REV3_QCSP_LIO_CFG(idx)); -+ else -+ before = qm_in(QCSP_LIO_CFG(idx)); -+ if (!done) { -+ liodn_offset = before & LIO_CFG_LIODN_MASK; -+ done = 1; -+ return; -+ } -+ after = (before & (~LIO_CFG_LIODN_MASK)) | liodn_offset; -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) -+ qm_out(REV3_QCSP_LIO_CFG(idx), after); -+ else -+ qm_out(QCSP_LIO_CFG(idx), after); -+} -+ -+#define IO_CFG_SDEST_MASK 0x00ff0000 -+int qman_set_sdest(u16 channel, unsigned int cpu_idx) -+{ -+ int idx = channel - QM_CHANNEL_SWPORTAL0; -+ u32 before, after; -+ -+ if (!qman_have_ccsr()) -+ return -ENODEV; -+ if ((qman_ip_rev & 0xFF00) == QMAN_REV31) { -+ /* LS1043A - only one L2 cache */ -+ cpu_idx = 0; -+ } -+ -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) { -+ before = qm_in(REV3_QCSP_IO_CFG(idx)); -+ /* Each pair of vcpu share the same SRQ(SDEST) */ -+ cpu_idx /= 2; -+ after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16); -+ qm_out(REV3_QCSP_IO_CFG(idx), after); -+ } else { -+ before = qm_in(QCSP_IO_CFG(idx)); -+ after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16); -+ qm_out(QCSP_IO_CFG(idx), after); -+ } -+ return 0; -+} -+ -+#define MISC_CFG_WPM_MASK 0x00000002 -+int qm_set_wpm(int wpm) -+{ -+ u32 before; -+ u32 after; -+ -+ if (!qman_have_ccsr()) -+ return -ENODEV; -+ -+ before = qm_in(MISC_CFG); -+ after = (before & (~MISC_CFG_WPM_MASK)) | (wpm << 1); -+ qm_out(MISC_CFG, after); -+ return 0; -+} -+ -+int qm_get_wpm(int *wpm) -+{ -+ u32 before; -+ -+ if (!qman_have_ccsr()) -+ return -ENODEV; -+ -+ before = qm_in(MISC_CFG); -+ *wpm = (before & MISC_CFG_WPM_MASK) >> 1; -+ return 0; -+} -+ -+/* CEETM_CFG_PRES register has PRES field which is calculated by: -+ * PRES = (2^22 / credit update reference period) * QMan clock period -+ * = (2^22 * 10^9)/ CONFIG_QMAN_CEETM_UPDATE_PERIOD) / qman_clk -+ */ -+ -+int qman_ceetm_set_prescaler(enum qm_dc_portal portal) -+{ -+ u64 temp; -+ u16 pres; -+ -+ if (!qman_have_ccsr()) -+ return -ENODEV; -+ -+ temp = 0x400000 * 100; -+ do_div(temp, CONFIG_QMAN_CEETM_UPDATE_PERIOD); -+ temp *= 10000000; -+ do_div(temp, qman_clk); -+ pres = (u16) temp; -+ qm_out(CEETM_CFG_IDX, portal); -+ qm_out(CEETM_CFG_PRES, pres); -+ return 0; -+} -+ -+int qman_ceetm_get_prescaler(u16 *pres) -+{ -+ if (!qman_have_ccsr()) -+ return -ENODEV; -+ *pres = (u16)qm_in(CEETM_CFG_PRES); -+ return 0; -+} -+ -+#define DCP_CFG_CEETME_MASK 0xFFFF0000 -+#define QM_SP_ENABLE_CEETM(n) (0x80000000 >> (n)) -+int qman_sp_enable_ceetm_mode(enum qm_dc_portal portal, u16 sub_portal) -+{ -+ u32 dcp_cfg; -+ -+ if (!qman_have_ccsr()) -+ return -ENODEV; -+ -+ dcp_cfg = qm_in(DCP_CFG(portal)); -+ dcp_cfg |= QM_SP_ENABLE_CEETM(sub_portal); -+ qm_out(DCP_CFG(portal), dcp_cfg); -+ return 0; -+} -+ -+int qman_sp_disable_ceetm_mode(enum qm_dc_portal portal, u16 sub_portal) -+{ -+ u32 dcp_cfg; -+ -+ if (!qman_have_ccsr()) -+ return -ENODEV; -+ dcp_cfg = qm_in(DCP_CFG(portal)); -+ dcp_cfg &= ~(QM_SP_ENABLE_CEETM(sub_portal)); -+ qm_out(DCP_CFG(portal), dcp_cfg); -+ return 0; -+} -+ -+int qman_ceetm_get_xsfdr(enum qm_dc_portal portal, unsigned int *num) -+{ -+ if (!qman_have_ccsr()) -+ return -ENODEV; -+ *num = qm_in(CEETM_XSFDR_IN_USE); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_get_xsfdr); -+ -+#ifdef CONFIG_SYSFS -+ -+#define DRV_NAME "fsl-qman" -+#define DCP_MAX_ID 3 -+#define DCP_MIN_ID 0 -+ -+static ssize_t show_pfdr_fpc(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(PFDR_FPC)); -+}; -+ -+static ssize_t show_dlm_avg(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ u32 data; -+ int i; -+ -+ if (!sscanf(dev_attr->attr.name, "dcp%d_dlm_avg", &i)) -+ return -EINVAL; -+ if (i < DCP_MIN_ID || i > DCP_MAX_ID) -+ return -EINVAL; -+ data = qm_in(DCP_DLM_AVG(i)); -+ return snprintf(buf, PAGE_SIZE, "%d.%08d\n", data>>8, -+ (data & 0x000000ff)*390625); -+}; -+ -+static ssize_t set_dlm_avg(struct device *dev, -+ struct device_attribute *dev_attr, const char *buf, size_t count) -+{ -+ unsigned long val; -+ int i; -+ -+ if (!sscanf(dev_attr->attr.name, "dcp%d_dlm_avg", &i)) -+ return -EINVAL; -+ if (i < DCP_MIN_ID || i > DCP_MAX_ID) -+ return -EINVAL; -+ if (kstrtoul(buf, 0, &val)) { -+ dev_dbg(dev, "invalid input %s\n", buf); -+ return -EINVAL; -+ } -+ qm_out(DCP_DLM_AVG(i), val); -+ return count; -+}; -+ -+static ssize_t show_pfdr_cfg(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(PFDR_CFG)); -+}; -+ -+static ssize_t set_pfdr_cfg(struct device *dev, -+ struct device_attribute *dev_attr, const char *buf, size_t count) -+{ -+ unsigned long val; -+ -+ if (kstrtoul(buf, 0, &val)) { -+ dev_dbg(dev, "invalid input %s\n", buf); -+ return -EINVAL; -+ } -+ qm_out(PFDR_CFG, val); -+ return count; -+}; -+ -+static ssize_t show_sfdr_in_use(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(SFDR_IN_USE)); -+}; -+ -+static ssize_t show_idle_stat(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(IDLE_STAT)); -+}; -+ -+static ssize_t show_ci_rlm_avg(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ u32 data = qm_in(CI_RLM_AVG); -+ return snprintf(buf, PAGE_SIZE, "%d.%08d\n", data>>8, -+ (data & 0x000000ff)*390625); -+}; -+ -+static ssize_t set_ci_rlm_avg(struct device *dev, -+ struct device_attribute *dev_attr, const char *buf, size_t count) -+{ -+ unsigned long val; -+ -+ if (kstrtoul(buf, 0, &val)) { -+ dev_dbg(dev, "invalid input %s\n", buf); -+ return -EINVAL; -+ } -+ qm_out(CI_RLM_AVG, val); -+ return count; -+}; -+ -+static ssize_t show_err_isr(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "0x%08x\n", qm_in(ERR_ISR)); -+}; -+ -+#define SBEC_MAX_ID 14 -+#define SBEC_MIN_ID 0 -+ -+static ssize_t show_sbec(struct device *dev, -+ struct device_attribute *dev_attr, char *buf) -+{ -+ int i; -+ -+ if (!sscanf(dev_attr->attr.name, "sbec_%d", &i)) -+ return -EINVAL; -+ if (i < SBEC_MIN_ID || i > SBEC_MAX_ID) -+ return -EINVAL; -+ return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(SBEC(i))); -+}; -+ -+static DEVICE_ATTR(pfdr_fpc, S_IRUSR, show_pfdr_fpc, NULL); -+static DEVICE_ATTR(pfdr_cfg, S_IRUSR, show_pfdr_cfg, set_pfdr_cfg); -+static DEVICE_ATTR(idle_stat, S_IRUSR, show_idle_stat, NULL); -+static DEVICE_ATTR(ci_rlm_avg, (S_IRUSR|S_IWUSR), -+ show_ci_rlm_avg, set_ci_rlm_avg); -+static DEVICE_ATTR(err_isr, S_IRUSR, show_err_isr, NULL); -+static DEVICE_ATTR(sfdr_in_use, S_IRUSR, show_sfdr_in_use, NULL); -+ -+static DEVICE_ATTR(dcp0_dlm_avg, (S_IRUSR|S_IWUSR), show_dlm_avg, set_dlm_avg); -+static DEVICE_ATTR(dcp1_dlm_avg, (S_IRUSR|S_IWUSR), show_dlm_avg, set_dlm_avg); -+static DEVICE_ATTR(dcp2_dlm_avg, (S_IRUSR|S_IWUSR), show_dlm_avg, set_dlm_avg); -+static DEVICE_ATTR(dcp3_dlm_avg, (S_IRUSR|S_IWUSR), show_dlm_avg, set_dlm_avg); -+ -+static DEVICE_ATTR(sbec_0, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_1, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_2, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_3, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_4, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_5, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_6, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_7, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_8, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_9, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_10, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_11, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_12, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_13, S_IRUSR, show_sbec, NULL); -+static DEVICE_ATTR(sbec_14, S_IRUSR, show_sbec, NULL); -+ -+static struct attribute *qman_dev_attributes[] = { -+ &dev_attr_pfdr_fpc.attr, -+ &dev_attr_pfdr_cfg.attr, -+ &dev_attr_idle_stat.attr, -+ &dev_attr_ci_rlm_avg.attr, -+ &dev_attr_err_isr.attr, -+ &dev_attr_dcp0_dlm_avg.attr, -+ &dev_attr_dcp1_dlm_avg.attr, -+ &dev_attr_dcp2_dlm_avg.attr, -+ &dev_attr_dcp3_dlm_avg.attr, -+ /* sfdr_in_use will be added if necessary */ -+ NULL -+}; -+ -+static struct attribute *qman_dev_ecr_attributes[] = { -+ &dev_attr_sbec_0.attr, -+ &dev_attr_sbec_1.attr, -+ &dev_attr_sbec_2.attr, -+ &dev_attr_sbec_3.attr, -+ &dev_attr_sbec_4.attr, -+ &dev_attr_sbec_5.attr, -+ &dev_attr_sbec_6.attr, -+ &dev_attr_sbec_7.attr, -+ &dev_attr_sbec_8.attr, -+ &dev_attr_sbec_9.attr, -+ &dev_attr_sbec_10.attr, -+ &dev_attr_sbec_11.attr, -+ &dev_attr_sbec_12.attr, -+ &dev_attr_sbec_13.attr, -+ &dev_attr_sbec_14.attr, -+ NULL -+}; -+ -+/* root level */ -+static const struct attribute_group qman_dev_attr_grp = { -+ .name = NULL, -+ .attrs = qman_dev_attributes -+}; -+static const struct attribute_group qman_dev_ecr_grp = { -+ .name = "error_capture", -+ .attrs = qman_dev_ecr_attributes -+}; -+ -+static int of_fsl_qman_remove(struct platform_device *ofdev) -+{ -+ sysfs_remove_group(&ofdev->dev.kobj, &qman_dev_attr_grp); -+ return 0; -+}; -+ -+static int of_fsl_qman_probe(struct platform_device *ofdev) -+{ -+ int ret; -+ -+ ret = sysfs_create_group(&ofdev->dev.kobj, &qman_dev_attr_grp); -+ if (ret) -+ goto done; -+ ret = sysfs_add_file_to_group(&ofdev->dev.kobj, -+ &dev_attr_sfdr_in_use.attr, qman_dev_attr_grp.name); -+ if (ret) -+ goto del_group_0; -+ ret = sysfs_create_group(&ofdev->dev.kobj, &qman_dev_ecr_grp); -+ if (ret) -+ goto del_group_0; -+ -+ goto done; -+ -+del_group_0: -+ sysfs_remove_group(&ofdev->dev.kobj, &qman_dev_attr_grp); -+done: -+ if (ret) -+ dev_err(&ofdev->dev, -+ "Cannot create dev attributes ret=%d\n", ret); -+ return ret; -+}; -+ -+static struct of_device_id of_fsl_qman_ids[] = { -+ { -+ .compatible = "fsl,qman", -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, of_fsl_qman_ids); -+ -+#ifdef CONFIG_SUSPEND -+ -+static u32 saved_isdr; -+static int qman_pm_suspend_noirq(struct device *dev) -+{ -+ uint32_t idle_state; -+ -+ suspend_unused_qportal(); -+ /* save isdr, disable all, clear isr */ -+ saved_isdr = qm_err_isr_disable_read(qm); -+ qm_err_isr_disable_write(qm, 0xffffffff); -+ qm_err_isr_status_clear(qm, 0xffffffff); -+ idle_state = qm_in(IDLE_STAT); -+ if (!(idle_state & 0x1)) { -+ pr_err("Qman not idle 0x%x aborting\n", idle_state); -+ qm_err_isr_disable_write(qm, saved_isdr); -+ resume_unused_qportal(); -+ return -EBUSY; -+ } -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Qman suspend code, IDLE_STAT = 0x%x\n", idle_state); -+#endif -+ return 0; -+} -+ -+static int qman_pm_resume_noirq(struct device *dev) -+{ -+ /* restore isdr */ -+ qm_err_isr_disable_write(qm, saved_isdr); -+ resume_unused_qportal(); -+ return 0; -+} -+#else -+#define qman_pm_suspend_noirq NULL -+#define qman_pm_resume_noirq NULL -+#endif -+ -+static const struct dev_pm_ops qman_pm_ops = { -+ .suspend_noirq = qman_pm_suspend_noirq, -+ .resume_noirq = qman_pm_resume_noirq, -+}; -+ -+static struct platform_driver of_fsl_qman_driver = { -+ .driver = { -+ .owner = THIS_MODULE, -+ .name = DRV_NAME, -+ .of_match_table = of_fsl_qman_ids, -+ .pm = &qman_pm_ops, -+ }, -+ .probe = of_fsl_qman_probe, -+ .remove = of_fsl_qman_remove, -+}; -+ -+static int qman_ctrl_init(void) -+{ -+ return platform_driver_register(&of_fsl_qman_driver); -+} -+ -+static void qman_ctrl_exit(void) -+{ -+ platform_driver_unregister(&of_fsl_qman_driver); -+} -+ -+module_init(qman_ctrl_init); -+module_exit(qman_ctrl_exit); -+ -+#endif /* CONFIG_SYSFS */ ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_debugfs.c -@@ -0,0 +1,1594 @@ -+/* Copyright 2010-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+#include "qman_private.h" -+ -+#define MAX_FQID (0x00ffffff) -+#define QM_FQD_BLOCK_SIZE 64 -+#define QM_FQD_AR (0xC10) -+ -+static u32 fqid_max; -+static u64 qman_ccsr_start; -+static u64 qman_ccsr_size; -+ -+static const char * const state_txt[] = { -+ "Out of Service", -+ "Retired", -+ "Tentatively Scheduled", -+ "Truly Scheduled", -+ "Parked", -+ "Active, Active Held or Held Suspended", -+ "Unknown State 6", -+ "Unknown State 7", -+ NULL, -+}; -+ -+static const u8 fqd_states[] = { -+ QM_MCR_NP_STATE_OOS, QM_MCR_NP_STATE_RETIRED, QM_MCR_NP_STATE_TEN_SCHED, -+ QM_MCR_NP_STATE_TRU_SCHED, QM_MCR_NP_STATE_PARKED, -+ QM_MCR_NP_STATE_ACTIVE}; -+ -+struct mask_to_text { -+ u16 mask; -+ const char *txt; -+}; -+ -+struct mask_filter_s { -+ u16 mask; -+ u8 filter; -+}; -+ -+static const struct mask_filter_s mask_filter[] = { -+ {QM_FQCTRL_PREFERINCACHE, 0}, -+ {QM_FQCTRL_PREFERINCACHE, 1}, -+ {QM_FQCTRL_HOLDACTIVE, 0}, -+ {QM_FQCTRL_HOLDACTIVE, 1}, -+ {QM_FQCTRL_AVOIDBLOCK, 0}, -+ {QM_FQCTRL_AVOIDBLOCK, 1}, -+ {QM_FQCTRL_FORCESFDR, 0}, -+ {QM_FQCTRL_FORCESFDR, 1}, -+ {QM_FQCTRL_CPCSTASH, 0}, -+ {QM_FQCTRL_CPCSTASH, 1}, -+ {QM_FQCTRL_CTXASTASHING, 0}, -+ {QM_FQCTRL_CTXASTASHING, 1}, -+ {QM_FQCTRL_ORP, 0}, -+ {QM_FQCTRL_ORP, 1}, -+ {QM_FQCTRL_TDE, 0}, -+ {QM_FQCTRL_TDE, 1}, -+ {QM_FQCTRL_CGE, 0}, -+ {QM_FQCTRL_CGE, 1} -+}; -+ -+static const struct mask_to_text fq_ctrl_text_list[] = { -+ { -+ .mask = QM_FQCTRL_PREFERINCACHE, -+ .txt = "Prefer in cache", -+ }, -+ { -+ .mask = QM_FQCTRL_HOLDACTIVE, -+ .txt = "Hold active in portal", -+ }, -+ { -+ .mask = QM_FQCTRL_AVOIDBLOCK, -+ .txt = "Avoid Blocking", -+ }, -+ { -+ .mask = QM_FQCTRL_FORCESFDR, -+ .txt = "High-priority SFDRs", -+ }, -+ { -+ .mask = QM_FQCTRL_CPCSTASH, -+ .txt = "CPC Stash Enable", -+ }, -+ { -+ .mask = QM_FQCTRL_CTXASTASHING, -+ .txt = "Context-A stashing", -+ }, -+ { -+ .mask = QM_FQCTRL_ORP, -+ .txt = "ORP Enable", -+ }, -+ { -+ .mask = QM_FQCTRL_TDE, -+ .txt = "Tail-Drop Enable", -+ }, -+ { -+ .mask = QM_FQCTRL_CGE, -+ .txt = "Congestion Group Enable", -+ }, -+ { -+ .mask = 0, -+ .txt = NULL, -+ } -+}; -+ -+static const char *get_fqd_ctrl_text(u16 mask) -+{ -+ int i = 0; -+ -+ while (fq_ctrl_text_list[i].txt != NULL) { -+ if (fq_ctrl_text_list[i].mask == mask) -+ return fq_ctrl_text_list[i].txt; -+ i++; -+ } -+ return NULL; -+} -+ -+static const struct mask_to_text stashing_text_list[] = { -+ { -+ .mask = QM_STASHING_EXCL_CTX, -+ .txt = "FQ Ctx Stash" -+ }, -+ { -+ .mask = QM_STASHING_EXCL_DATA, -+ .txt = "Frame Data Stash", -+ }, -+ { -+ .mask = QM_STASHING_EXCL_ANNOTATION, -+ .txt = "Frame Annotation Stash", -+ }, -+ { -+ .mask = 0, -+ .txt = NULL, -+ }, -+}; -+ -+static int user_input_convert(const char __user *user_buf, size_t count, -+ unsigned long *val) -+{ -+ char buf[12]; -+ -+ if (count > sizeof(buf) - 1) -+ return -EINVAL; -+ if (copy_from_user(buf, user_buf, count)) -+ return -EFAULT; -+ buf[count] = '\0'; -+ if (kstrtoul(buf, 0, val)) -+ return -EINVAL; -+ return 0; -+} -+ -+struct line_buffer_fq { -+ u32 buf[8]; -+ u32 buf_cnt; -+ int line_cnt; -+}; -+ -+static void add_to_line_buffer(struct line_buffer_fq *line_buf, u32 fqid, -+ struct seq_file *file) -+{ -+ line_buf->buf[line_buf->buf_cnt] = fqid; -+ line_buf->buf_cnt++; -+ if (line_buf->buf_cnt == 8) { -+ /* Buffer is full, flush it */ -+ if (line_buf->line_cnt != 0) -+ seq_puts(file, ",\n"); -+ seq_printf(file, "0x%06x,0x%06x,0x%06x,0x%06x,0x%06x," -+ "0x%06x,0x%06x,0x%06x", -+ line_buf->buf[0], line_buf->buf[1], line_buf->buf[2], -+ line_buf->buf[3], line_buf->buf[4], line_buf->buf[5], -+ line_buf->buf[6], line_buf->buf[7]); -+ line_buf->buf_cnt = 0; -+ line_buf->line_cnt++; -+ } -+} -+ -+static void flush_line_buffer(struct line_buffer_fq *line_buf, -+ struct seq_file *file) -+{ -+ if (line_buf->buf_cnt) { -+ int y = 0; -+ if (line_buf->line_cnt != 0) -+ seq_puts(file, ",\n"); -+ while (y != line_buf->buf_cnt) { -+ if (y+1 == line_buf->buf_cnt) -+ seq_printf(file, "0x%06x", line_buf->buf[y]); -+ else -+ seq_printf(file, "0x%06x,", line_buf->buf[y]); -+ y++; -+ } -+ line_buf->line_cnt++; -+ } -+ if (line_buf->line_cnt) -+ seq_putc(file, '\n'); -+} -+ -+static struct dentry *dfs_root; /* debugfs root directory */ -+ -+/******************************************************************************* -+ * Query Frame Queue Non Programmable Fields -+ ******************************************************************************/ -+struct query_fq_np_fields_data_s { -+ u32 fqid; -+}; -+static struct query_fq_np_fields_data_s query_fq_np_fields_data = { -+ .fqid = 1, -+}; -+ -+static int query_fq_np_fields_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct qm_mcr_queryfq_np np; -+ struct qman_fq fq; -+ -+ fq.fqid = query_fq_np_fields_data.fqid; -+ ret = qman_query_fq_np(&fq, &np); -+ if (ret) -+ return ret; -+ /* Print state */ -+ seq_printf(file, "Query FQ Non Programmable Fields Result fqid 0x%x\n", -+ fq.fqid); -+ seq_printf(file, " force eligible pending: %s\n", -+ (np.state & QM_MCR_NP_STATE_FE) ? "yes" : "no"); -+ seq_printf(file, " retirement pending: %s\n", -+ (np.state & QM_MCR_NP_STATE_R) ? "yes" : "no"); -+ seq_printf(file, " state: %s\n", -+ state_txt[np.state & QM_MCR_NP_STATE_MASK]); -+ seq_printf(file, " fq_link: 0x%x\n", np.fqd_link); -+ seq_printf(file, " odp_seq: %u\n", np.odp_seq); -+ seq_printf(file, " orp_nesn: %u\n", np.orp_nesn); -+ seq_printf(file, " orp_ea_hseq: %u\n", np.orp_ea_hseq); -+ seq_printf(file, " orp_ea_tseq: %u\n", np.orp_ea_tseq); -+ seq_printf(file, " orp_ea_hptr: 0x%x\n", np.orp_ea_hptr); -+ seq_printf(file, " orp_ea_tptr: 0x%x\n", np.orp_ea_tptr); -+ seq_printf(file, " pfdr_hptr: 0x%x\n", np.pfdr_hptr); -+ seq_printf(file, " pfdr_tptr: 0x%x\n", np.pfdr_tptr); -+ seq_printf(file, " is: ics_surp contains a %s\n", -+ (np.is) ? "deficit" : "surplus"); -+ seq_printf(file, " ics_surp: %u\n", np.ics_surp); -+ seq_printf(file, " byte_cnt: %u\n", np.byte_cnt); -+ seq_printf(file, " frm_cnt: %u\n", np.frm_cnt); -+ seq_printf(file, " ra1_sfdr: 0x%x\n", np.ra1_sfdr); -+ seq_printf(file, " ra2_sfdr: 0x%x\n", np.ra2_sfdr); -+ seq_printf(file, " od1_sfdr: 0x%x\n", np.od1_sfdr); -+ seq_printf(file, " od2_sfdr: 0x%x\n", np.od2_sfdr); -+ seq_printf(file, " od3_sfdr: 0x%x\n", np.od3_sfdr); -+ return 0; -+} -+ -+static int query_fq_np_fields_open(struct inode *inode, -+ struct file *file) -+{ -+ return single_open(file, query_fq_np_fields_show, NULL); -+} -+ -+static ssize_t query_fq_np_fields_write(struct file *f, -+ const char __user *buf, size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ if (val > MAX_FQID) -+ return -EINVAL; -+ query_fq_np_fields_data.fqid = (u32)val; -+ return count; -+} -+ -+static const struct file_operations query_fq_np_fields_fops = { -+ .owner = THIS_MODULE, -+ .open = query_fq_np_fields_open, -+ .read = seq_read, -+ .write = query_fq_np_fields_write, -+ .release = single_release, -+}; -+ -+/******************************************************************************* -+ * Frame Queue Programmable Fields -+ ******************************************************************************/ -+struct query_fq_fields_data_s { -+ u32 fqid; -+}; -+ -+static struct query_fq_fields_data_s query_fq_fields_data = { -+ .fqid = 1, -+}; -+ -+static int query_fq_fields_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct qm_fqd fqd; -+ struct qman_fq fq; -+ int i = 0; -+ -+ memset(&fqd, 0, sizeof(struct qm_fqd)); -+ fq.fqid = query_fq_fields_data.fqid; -+ ret = qman_query_fq(&fq, &fqd); -+ if (ret) -+ return ret; -+ seq_printf(file, "Query FQ Programmable Fields Result fqid 0x%x\n", -+ fq.fqid); -+ seq_printf(file, " orprws: %u\n", fqd.orprws); -+ seq_printf(file, " oa: %u\n", fqd.oa); -+ seq_printf(file, " olws: %u\n", fqd.olws); -+ -+ seq_printf(file, " cgid: %u\n", fqd.cgid); -+ -+ if ((fqd.fq_ctrl & QM_FQCTRL_MASK) == 0) -+ seq_puts(file, " fq_ctrl: None\n"); -+ else { -+ i = 0; -+ seq_puts(file, " fq_ctrl:\n"); -+ while (fq_ctrl_text_list[i].txt != NULL) { -+ if ((fqd.fq_ctrl & QM_FQCTRL_MASK) & -+ fq_ctrl_text_list[i].mask) -+ seq_printf(file, " %s\n", -+ fq_ctrl_text_list[i].txt); -+ i++; -+ } -+ } -+ seq_printf(file, " dest_channel: %u\n", fqd.dest.channel); -+ seq_printf(file, " dest_wq: %u\n", fqd.dest.wq); -+ seq_printf(file, " ics_cred: %u\n", fqd.ics_cred); -+ seq_printf(file, " td_mant: %u\n", fqd.td.mant); -+ seq_printf(file, " td_exp: %u\n", fqd.td.exp); -+ -+ seq_printf(file, " ctx_b: 0x%x\n", fqd.context_b); -+ -+ seq_printf(file, " ctx_a: 0x%llx\n", qm_fqd_stashing_get64(&fqd)); -+ /* Any stashing configured */ -+ if ((fqd.context_a.stashing.exclusive & 0x7) == 0) -+ seq_puts(file, " ctx_a_stash_exclusive: None\n"); -+ else { -+ seq_puts(file, " ctx_a_stash_exclusive:\n"); -+ i = 0; -+ while (stashing_text_list[i].txt != NULL) { -+ if ((fqd.fq_ctrl & 0x7) & stashing_text_list[i].mask) -+ seq_printf(file, " %s\n", -+ stashing_text_list[i].txt); -+ i++; -+ } -+ } -+ seq_printf(file, " ctx_a_stash_annotation_cl: %u\n", -+ fqd.context_a.stashing.annotation_cl); -+ seq_printf(file, " ctx_a_stash_data_cl: %u\n", -+ fqd.context_a.stashing.data_cl); -+ seq_printf(file, " ctx_a_stash_context_cl: %u\n", -+ fqd.context_a.stashing.context_cl); -+ return 0; -+} -+ -+static int query_fq_fields_open(struct inode *inode, -+ struct file *file) -+{ -+ return single_open(file, query_fq_fields_show, NULL); -+} -+ -+static ssize_t query_fq_fields_write(struct file *f, -+ const char __user *buf, size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ if (val > MAX_FQID) -+ return -EINVAL; -+ query_fq_fields_data.fqid = (u32)val; -+ return count; -+} -+ -+static const struct file_operations query_fq_fields_fops = { -+ .owner = THIS_MODULE, -+ .open = query_fq_fields_open, -+ .read = seq_read, -+ .write = query_fq_fields_write, -+ .release = single_release, -+}; -+ -+/******************************************************************************* -+ * Query WQ lengths -+ ******************************************************************************/ -+struct query_wq_lengths_data_s { -+ union { -+ u16 channel_wq; /* ignores wq (3 lsbits) */ -+ struct { -+ u16 id:13; /* qm_channel */ -+ u16 __reserved:3; -+ } __packed channel; -+ }; -+}; -+static struct query_wq_lengths_data_s query_wq_lengths_data; -+static int query_wq_lengths_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct qm_mcr_querywq wq; -+ int i; -+ -+ memset(&wq, 0, sizeof(struct qm_mcr_querywq)); -+ wq.channel.id = query_wq_lengths_data.channel.id; -+ ret = qman_query_wq(0, &wq); -+ if (ret) -+ return ret; -+ seq_printf(file, "Query Result For Channel: 0x%x\n", wq.channel.id); -+ for (i = 0; i < 8; i++) -+ /* mask out upper 4 bits since they are not part of length */ -+ seq_printf(file, " wq%d_len : %u\n", i, wq.wq_len[i] & 0x0fff); -+ return 0; -+} -+ -+static int query_wq_lengths_open(struct inode *inode, -+ struct file *file) -+{ -+ return single_open(file, query_wq_lengths_show, NULL); -+} -+ -+static ssize_t query_wq_lengths_write(struct file *f, -+ const char __user *buf, size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ if (val > 0xfff8) -+ return -EINVAL; -+ query_wq_lengths_data.channel.id = (u16)val; -+ return count; -+} -+ -+static const struct file_operations query_wq_lengths_fops = { -+ .owner = THIS_MODULE, -+ .open = query_wq_lengths_open, -+ .read = seq_read, -+ .write = query_wq_lengths_write, -+ .release = single_release, -+}; -+ -+/******************************************************************************* -+ * Query CGR -+ ******************************************************************************/ -+struct query_cgr_s { -+ u8 cgid; -+}; -+static struct query_cgr_s query_cgr_data; -+ -+static int query_cgr_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct qm_mcr_querycgr cgrd; -+ struct qman_cgr cgr; -+ int i, j; -+ u32 mask; -+ -+ memset(&cgr, 0, sizeof(cgr)); -+ memset(&cgrd, 0, sizeof(cgrd)); -+ cgr.cgrid = query_cgr_data.cgid; -+ ret = qman_query_cgr(&cgr, &cgrd); -+ if (ret) -+ return ret; -+ seq_printf(file, "Query CGR id 0x%x\n", cgr.cgrid); -+ seq_printf(file, " wr_parm_g MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ cgrd.cgr.wr_parm_g.MA, cgrd.cgr.wr_parm_g.Mn, -+ cgrd.cgr.wr_parm_g.SA, cgrd.cgr.wr_parm_g.Sn, -+ cgrd.cgr.wr_parm_g.Pn); -+ -+ seq_printf(file, " wr_parm_y MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ cgrd.cgr.wr_parm_y.MA, cgrd.cgr.wr_parm_y.Mn, -+ cgrd.cgr.wr_parm_y.SA, cgrd.cgr.wr_parm_y.Sn, -+ cgrd.cgr.wr_parm_y.Pn); -+ -+ seq_printf(file, " wr_parm_r MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ cgrd.cgr.wr_parm_r.MA, cgrd.cgr.wr_parm_r.Mn, -+ cgrd.cgr.wr_parm_r.SA, cgrd.cgr.wr_parm_r.Sn, -+ cgrd.cgr.wr_parm_r.Pn); -+ -+ seq_printf(file, " wr_en_g: %u, wr_en_y: %u, we_en_r: %u\n", -+ cgrd.cgr.wr_en_g, cgrd.cgr.wr_en_y, cgrd.cgr.wr_en_r); -+ -+ seq_printf(file, " cscn_en: %u\n", cgrd.cgr.cscn_en); -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) { -+ seq_puts(file, " cscn_targ_dcp:\n"); -+ mask = 0x80000000; -+ for (i = 0; i < 32; i++) { -+ if (cgrd.cgr.cscn_targ & mask) -+ seq_printf(file, " send CSCN to dcp %u\n", -+ (31 - i)); -+ mask >>= 1; -+ } -+ -+ seq_puts(file, " cscn_targ_swp:\n"); -+ for (i = 0; i < 4; i++) { -+ mask = 0x80000000; -+ for (j = 0; j < 32; j++) { -+ if (cgrd.cscn_targ_swp[i] & mask) -+ seq_printf(file, " send CSCN to swp" -+ " %u\n", (127 - (i * 32) - j)); -+ mask >>= 1; -+ } -+ } -+ } else { -+ seq_printf(file, " cscn_targ: %u\n", cgrd.cgr.cscn_targ); -+ } -+ seq_printf(file, " cstd_en: %u\n", cgrd.cgr.cstd_en); -+ seq_printf(file, " cs: %u\n", cgrd.cgr.cs); -+ -+ seq_printf(file, " cs_thresh_TA: %u, cs_thresh_Tn: %u\n", -+ cgrd.cgr.cs_thres.TA, cgrd.cgr.cs_thres.Tn); -+ -+ seq_printf(file, " mode: %s\n", -+ (cgrd.cgr.mode & QMAN_CGR_MODE_FRAME) ? -+ "frame count" : "byte count"); -+ seq_printf(file, " i_bcnt: %llu\n", qm_mcr_querycgr_i_get64(&cgrd)); -+ seq_printf(file, " a_bcnt: %llu\n", qm_mcr_querycgr_a_get64(&cgrd)); -+ -+ return 0; -+} -+ -+static int query_cgr_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, query_cgr_show, NULL); -+} -+ -+static ssize_t query_cgr_write(struct file *f, const char __user *buf, -+ size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ if (val > 0xff) -+ return -EINVAL; -+ query_cgr_data.cgid = (u8)val; -+ return count; -+} -+ -+static const struct file_operations query_cgr_fops = { -+ .owner = THIS_MODULE, -+ .open = query_cgr_open, -+ .read = seq_read, -+ .write = query_cgr_write, -+ .release = single_release, -+}; -+ -+/******************************************************************************* -+ * Test Write CGR -+ ******************************************************************************/ -+struct test_write_cgr_s { -+ u64 i_bcnt; -+ u8 cgid; -+}; -+static struct test_write_cgr_s test_write_cgr_data; -+ -+static int testwrite_cgr_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct qm_mcr_cgrtestwrite result; -+ struct qman_cgr cgr; -+ u64 i_bcnt; -+ -+ memset(&cgr, 0, sizeof(struct qman_cgr)); -+ memset(&result, 0, sizeof(struct qm_mcr_cgrtestwrite)); -+ cgr.cgrid = test_write_cgr_data.cgid; -+ i_bcnt = test_write_cgr_data.i_bcnt; -+ ret = qman_testwrite_cgr(&cgr, i_bcnt, &result); -+ if (ret) -+ return ret; -+ seq_printf(file, "CGR Test Write CGR id 0x%x\n", cgr.cgrid); -+ seq_printf(file, " wr_parm_g MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ result.cgr.wr_parm_g.MA, result.cgr.wr_parm_g.Mn, -+ result.cgr.wr_parm_g.SA, result.cgr.wr_parm_g.Sn, -+ result.cgr.wr_parm_g.Pn); -+ seq_printf(file, " wr_parm_y MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ result.cgr.wr_parm_y.MA, result.cgr.wr_parm_y.Mn, -+ result.cgr.wr_parm_y.SA, result.cgr.wr_parm_y.Sn, -+ result.cgr.wr_parm_y.Pn); -+ seq_printf(file, " wr_parm_r MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ result.cgr.wr_parm_r.MA, result.cgr.wr_parm_r.Mn, -+ result.cgr.wr_parm_r.SA, result.cgr.wr_parm_r.Sn, -+ result.cgr.wr_parm_r.Pn); -+ seq_printf(file, " wr_en_g: %u, wr_en_y: %u, we_en_r: %u\n", -+ result.cgr.wr_en_g, result.cgr.wr_en_y, result.cgr.wr_en_r); -+ seq_printf(file, " cscn_en: %u\n", result.cgr.cscn_en); -+ seq_printf(file, " cscn_targ: %u\n", result.cgr.cscn_targ); -+ seq_printf(file, " cstd_en: %u\n", result.cgr.cstd_en); -+ seq_printf(file, " cs: %u\n", result.cgr.cs); -+ seq_printf(file, " cs_thresh_TA: %u, cs_thresh_Tn: %u\n", -+ result.cgr.cs_thres.TA, result.cgr.cs_thres.Tn); -+ -+ /* Add Mode for Si 2 */ -+ seq_printf(file, " mode: %s\n", -+ (result.cgr.mode & QMAN_CGR_MODE_FRAME) ? -+ "frame count" : "byte count"); -+ -+ seq_printf(file, " i_bcnt: %llu\n", -+ qm_mcr_cgrtestwrite_i_get64(&result)); -+ seq_printf(file, " a_bcnt: %llu\n", -+ qm_mcr_cgrtestwrite_a_get64(&result)); -+ seq_printf(file, " wr_prob_g: %u\n", result.wr_prob_g); -+ seq_printf(file, " wr_prob_y: %u\n", result.wr_prob_y); -+ seq_printf(file, " wr_prob_r: %u\n", result.wr_prob_r); -+ return 0; -+} -+ -+static int testwrite_cgr_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, testwrite_cgr_show, NULL); -+} -+ -+static const struct file_operations testwrite_cgr_fops = { -+ .owner = THIS_MODULE, -+ .open = testwrite_cgr_open, -+ .read = seq_read, -+ .release = single_release, -+}; -+ -+ -+static int testwrite_cgr_ibcnt_show(struct seq_file *file, void *offset) -+{ -+ seq_printf(file, "i_bcnt: %llu\n", test_write_cgr_data.i_bcnt); -+ return 0; -+} -+static int testwrite_cgr_ibcnt_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, testwrite_cgr_ibcnt_show, NULL); -+} -+ -+static ssize_t testwrite_cgr_ibcnt_write(struct file *f, const char __user *buf, -+ size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ test_write_cgr_data.i_bcnt = val; -+ return count; -+} -+ -+static const struct file_operations teswrite_cgr_ibcnt_fops = { -+ .owner = THIS_MODULE, -+ .open = testwrite_cgr_ibcnt_open, -+ .read = seq_read, -+ .write = testwrite_cgr_ibcnt_write, -+ .release = single_release, -+}; -+ -+static int testwrite_cgr_cgrid_show(struct seq_file *file, void *offset) -+{ -+ seq_printf(file, "cgrid: %u\n", (u32)test_write_cgr_data.cgid); -+ return 0; -+} -+static int testwrite_cgr_cgrid_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, testwrite_cgr_cgrid_show, NULL); -+} -+ -+static ssize_t testwrite_cgr_cgrid_write(struct file *f, const char __user *buf, -+ size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ if (val > 0xff) -+ return -EINVAL; -+ test_write_cgr_data.cgid = (u8)val; -+ return count; -+} -+ -+static const struct file_operations teswrite_cgr_cgrid_fops = { -+ .owner = THIS_MODULE, -+ .open = testwrite_cgr_cgrid_open, -+ .read = seq_read, -+ .write = testwrite_cgr_cgrid_write, -+ .release = single_release, -+}; -+ -+/******************************************************************************* -+ * Query Congestion State -+ ******************************************************************************/ -+static int query_congestion_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct qm_mcr_querycongestion cs; -+ int i, j, in_cong = 0; -+ u32 mask; -+ -+ memset(&cs, 0, sizeof(struct qm_mcr_querycongestion)); -+ ret = qman_query_congestion(&cs); -+ if (ret) -+ return ret; -+ seq_puts(file, "Query Congestion Result\n"); -+ for (i = 0; i < 8; i++) { -+ mask = 0x80000000; -+ for (j = 0; j < 32; j++) { -+ if (cs.state.__state[i] & mask) { -+ in_cong = 1; -+ seq_printf(file, " cg %u: %s\n", (i*32)+j, -+ "in congestion"); -+ } -+ mask >>= 1; -+ } -+ } -+ if (!in_cong) -+ seq_puts(file, " All congestion groups not congested.\n"); -+ return 0; -+} -+ -+static int query_congestion_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, query_congestion_show, NULL); -+} -+ -+static const struct file_operations query_congestion_fops = { -+ .owner = THIS_MODULE, -+ .open = query_congestion_open, -+ .read = seq_read, -+ .release = single_release, -+}; -+ -+/******************************************************************************* -+ * Query CCGR -+ ******************************************************************************/ -+struct query_ccgr_s { -+ u32 ccgid; -+}; -+static struct query_ccgr_s query_ccgr_data; -+ -+static int query_ccgr_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct qm_mcr_ceetm_ccgr_query ccgr_query; -+ struct qm_mcc_ceetm_ccgr_query query_opts; -+ int i, j; -+ u32 mask; -+ -+ memset(&ccgr_query, 0, sizeof(struct qm_mcr_ceetm_ccgr_query)); -+ memset(&query_opts, 0, sizeof(struct qm_mcc_ceetm_ccgr_query)); -+ -+ if ((qman_ip_rev & 0xFF00) < QMAN_REV30) -+ return -EINVAL; -+ -+ seq_printf(file, "Query CCGID %x\n", query_ccgr_data.ccgid); -+ query_opts.dcpid = ((query_ccgr_data.ccgid & 0xFF000000) >> 24); -+ query_opts.ccgrid = query_ccgr_data.ccgid & 0x000001FF; -+ ret = qman_ceetm_query_ccgr(&query_opts, &ccgr_query); -+ if (ret) -+ return ret; -+ seq_printf(file, "Query CCGR id %x in DCP %d\n", query_opts.ccgrid, -+ query_opts.dcpid); -+ seq_printf(file, " wr_parm_g MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ ccgr_query.cm_query.wr_parm_g.MA, -+ ccgr_query.cm_query.wr_parm_g.Mn, -+ ccgr_query.cm_query.wr_parm_g.SA, -+ ccgr_query.cm_query.wr_parm_g.Sn, -+ ccgr_query.cm_query.wr_parm_g.Pn); -+ -+ seq_printf(file, " wr_parm_y MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ ccgr_query.cm_query.wr_parm_y.MA, -+ ccgr_query.cm_query.wr_parm_y.Mn, -+ ccgr_query.cm_query.wr_parm_y.SA, -+ ccgr_query.cm_query.wr_parm_y.Sn, -+ ccgr_query.cm_query.wr_parm_y.Pn); -+ -+ seq_printf(file, " wr_parm_r MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n", -+ ccgr_query.cm_query.wr_parm_r.MA, -+ ccgr_query.cm_query.wr_parm_r.Mn, -+ ccgr_query.cm_query.wr_parm_r.SA, -+ ccgr_query.cm_query.wr_parm_r.Sn, -+ ccgr_query.cm_query.wr_parm_r.Pn); -+ -+ seq_printf(file, " wr_en_g: %u, wr_en_y: %u, we_en_r: %u\n", -+ ccgr_query.cm_query.ctl_wr_en_g, -+ ccgr_query.cm_query.ctl_wr_en_y, -+ ccgr_query.cm_query.ctl_wr_en_r); -+ -+ seq_printf(file, " cscn_en: %u\n", ccgr_query.cm_query.ctl_cscn_en); -+ seq_puts(file, " cscn_targ_dcp:\n"); -+ mask = 0x80000000; -+ for (i = 0; i < 32; i++) { -+ if (ccgr_query.cm_query.cscn_targ_dcp & mask) -+ seq_printf(file, " send CSCN to dcp %u\n", (31 - i)); -+ mask >>= 1; -+ } -+ -+ seq_puts(file, " cscn_targ_swp:\n"); -+ for (i = 0; i < 4; i++) { -+ mask = 0x80000000; -+ for (j = 0; j < 32; j++) { -+ if (ccgr_query.cm_query.cscn_targ_swp[i] & mask) -+ seq_printf(file, " send CSCN to swp" -+ "%u\n", (127 - (i * 32) - j)); -+ mask >>= 1; -+ } -+ } -+ -+ seq_printf(file, " td_en: %u\n", ccgr_query.cm_query.ctl_td_en); -+ -+ seq_printf(file, " cs_thresh_in_TA: %u, cs_thresh_in_Tn: %u\n", -+ ccgr_query.cm_query.cs_thres.TA, -+ ccgr_query.cm_query.cs_thres.Tn); -+ -+ seq_printf(file, " cs_thresh_out_TA: %u, cs_thresh_out_Tn: %u\n", -+ ccgr_query.cm_query.cs_thres_x.TA, -+ ccgr_query.cm_query.cs_thres_x.Tn); -+ -+ seq_printf(file, " td_thresh_TA: %u, td_thresh_Tn: %u\n", -+ ccgr_query.cm_query.td_thres.TA, -+ ccgr_query.cm_query.td_thres.Tn); -+ -+ seq_printf(file, " mode: %s\n", -+ (ccgr_query.cm_query.ctl_mode & -+ QMAN_CGR_MODE_FRAME) ? -+ "frame count" : "byte count"); -+ seq_printf(file, " i_cnt: %llu\n", (u64)ccgr_query.cm_query.i_cnt); -+ seq_printf(file, " a_cnt: %llu\n", (u64)ccgr_query.cm_query.a_cnt); -+ -+ return 0; -+} -+ -+static int query_ccgr_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, query_ccgr_show, NULL); -+} -+ -+static ssize_t query_ccgr_write(struct file *f, const char __user *buf, -+ size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ query_ccgr_data.ccgid = val; -+ return count; -+} -+ -+static const struct file_operations query_ccgr_fops = { -+ .owner = THIS_MODULE, -+ .open = query_ccgr_open, -+ .read = seq_read, -+ .write = query_ccgr_write, -+ .release = single_release, -+}; -+/******************************************************************************* -+ * QMan register -+ ******************************************************************************/ -+struct qman_register_s { -+ u32 val; -+}; -+static struct qman_register_s qman_register_data; -+ -+static void init_ccsrmempeek(void) -+{ -+ struct device_node *dn; -+ const u32 *regaddr_p; -+ -+ dn = of_find_compatible_node(NULL, NULL, "fsl,qman"); -+ if (!dn) { -+ pr_info("No fsl,qman node\n"); -+ return; -+ } -+ regaddr_p = of_get_address(dn, 0, &qman_ccsr_size, NULL); -+ if (!regaddr_p) { -+ of_node_put(dn); -+ return; -+ } -+ qman_ccsr_start = of_translate_address(dn, regaddr_p); -+ of_node_put(dn); -+} -+/* This function provides access to QMan ccsr memory map */ -+static int qman_ccsrmempeek(u32 *val, u32 offset) -+{ -+ void __iomem *addr; -+ u64 phys_addr; -+ -+ if (!qman_ccsr_start) -+ return -EINVAL; -+ -+ if (offset > (qman_ccsr_size - sizeof(u32))) -+ return -EINVAL; -+ -+ phys_addr = qman_ccsr_start + offset; -+ addr = ioremap(phys_addr, sizeof(u32)); -+ if (!addr) { -+ pr_err("ccsrmempeek, ioremap failed\n"); -+ return -EINVAL; -+ } -+ *val = in_be32(addr); -+ iounmap(addr); -+ return 0; -+} -+ -+static int qman_ccsrmempeek_show(struct seq_file *file, void *offset) -+{ -+ u32 b; -+ -+ qman_ccsrmempeek(&b, qman_register_data.val); -+ seq_printf(file, "QMan register offset = 0x%x\n", -+ qman_register_data.val); -+ seq_printf(file, "value = 0x%08x\n", b); -+ -+ return 0; -+} -+ -+static int qman_ccsrmempeek_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, qman_ccsrmempeek_show, NULL); -+} -+ -+static ssize_t qman_ccsrmempeek_write(struct file *f, const char __user *buf, -+ size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ /* multiple of 4 */ -+ if (val > (qman_ccsr_size - sizeof(u32))) { -+ pr_info("Input 0x%lx > 0x%llx\n", -+ val, (qman_ccsr_size - sizeof(u32))); -+ return -EINVAL; -+ } -+ if (val & 0x3) { -+ pr_info("Input 0x%lx not multiple of 4\n", val); -+ return -EINVAL; -+ } -+ qman_register_data.val = val; -+ return count; -+} -+ -+static const struct file_operations qman_ccsrmempeek_fops = { -+ .owner = THIS_MODULE, -+ .open = qman_ccsrmempeek_open, -+ .read = seq_read, -+ .write = qman_ccsrmempeek_write, -+}; -+ -+/******************************************************************************* -+ * QMan state -+ ******************************************************************************/ -+static int qman_fqd_state_show(struct seq_file *file, void *offset) -+{ -+ struct qm_mcr_queryfq_np np; -+ struct qman_fq fq; -+ struct line_buffer_fq line_buf; -+ int ret, i; -+ u8 *state = file->private; -+ u32 qm_fq_state_cnt[ARRAY_SIZE(fqd_states)]; -+ -+ memset(qm_fq_state_cnt, 0, sizeof(qm_fq_state_cnt)); -+ memset(&line_buf, 0, sizeof(line_buf)); -+ -+ seq_printf(file, "List of fq ids in state: %s\n", state_txt[*state]); -+ -+ for (i = 1; i < fqid_max; i++) { -+ fq.fqid = i; -+ ret = qman_query_fq_np(&fq, &np); -+ if (ret) -+ return ret; -+ if (*state == (np.state & QM_MCR_NP_STATE_MASK)) -+ add_to_line_buffer(&line_buf, fq.fqid, file); -+ /* Keep a summary count of all states */ -+ if ((np.state & QM_MCR_NP_STATE_MASK) < ARRAY_SIZE(fqd_states)) -+ qm_fq_state_cnt[(np.state & QM_MCR_NP_STATE_MASK)]++; -+ } -+ flush_line_buffer(&line_buf, file); -+ -+ for (i = 0; i < ARRAY_SIZE(fqd_states); i++) { -+ seq_printf(file, "%s count = %u\n", state_txt[i], -+ qm_fq_state_cnt[i]); -+ } -+ return 0; -+} -+ -+static int qman_fqd_state_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, qman_fqd_state_show, inode->i_private); -+} -+ -+static const struct file_operations qman_fqd_state_fops = { -+ .owner = THIS_MODULE, -+ .open = qman_fqd_state_open, -+ .read = seq_read, -+}; -+ -+static int qman_fqd_ctrl_show(struct seq_file *file, void *offset) -+{ -+ struct qm_fqd fqd; -+ struct qman_fq fq; -+ u32 fq_en_cnt = 0, fq_di_cnt = 0; -+ int ret, i; -+ struct mask_filter_s *data = file->private; -+ const char *ctrl_txt = get_fqd_ctrl_text(data->mask); -+ struct line_buffer_fq line_buf; -+ -+ memset(&line_buf, 0, sizeof(line_buf)); -+ seq_printf(file, "List of fq ids with: %s :%s\n", -+ ctrl_txt, (data->filter) ? "enabled" : "disabled"); -+ for (i = 1; i < fqid_max; i++) { -+ fq.fqid = i; -+ memset(&fqd, 0, sizeof(struct qm_fqd)); -+ ret = qman_query_fq(&fq, &fqd); -+ if (ret) -+ return ret; -+ if (data->filter) { -+ if (fqd.fq_ctrl & data->mask) -+ add_to_line_buffer(&line_buf, fq.fqid, file); -+ } else { -+ if (!(fqd.fq_ctrl & data->mask)) -+ add_to_line_buffer(&line_buf, fq.fqid, file); -+ } -+ if (fqd.fq_ctrl & data->mask) -+ fq_en_cnt++; -+ else -+ fq_di_cnt++; -+ } -+ flush_line_buffer(&line_buf, file); -+ -+ seq_printf(file, "Total FQD with: %s : enabled = %u\n", -+ ctrl_txt, fq_en_cnt); -+ seq_printf(file, "Total FQD with: %s : disabled = %u\n", -+ ctrl_txt, fq_di_cnt); -+ return 0; -+} -+ -+/******************************************************************************* -+ * QMan ctrl CGE, TDE, ORP, CTX, CPC, SFDR, BLOCK, HOLD, CACHE -+ ******************************************************************************/ -+static int qman_fqd_ctrl_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, qman_fqd_ctrl_show, inode->i_private); -+} -+ -+static const struct file_operations qman_fqd_ctrl_fops = { -+ .owner = THIS_MODULE, -+ .open = qman_fqd_ctrl_open, -+ .read = seq_read, -+}; -+ -+/******************************************************************************* -+ * QMan ctrl summary -+ ******************************************************************************/ -+/******************************************************************************* -+ * QMan summary state -+ ******************************************************************************/ -+static int qman_fqd_non_prog_summary_show(struct seq_file *file, void *offset) -+{ -+ struct qm_mcr_queryfq_np np; -+ struct qman_fq fq; -+ int ret, i; -+ u32 qm_fq_state_cnt[ARRAY_SIZE(fqd_states)]; -+ -+ memset(qm_fq_state_cnt, 0, sizeof(qm_fq_state_cnt)); -+ -+ for (i = 1; i < fqid_max; i++) { -+ fq.fqid = i; -+ ret = qman_query_fq_np(&fq, &np); -+ if (ret) -+ return ret; -+ /* Keep a summary count of all states */ -+ if ((np.state & QM_MCR_NP_STATE_MASK) < ARRAY_SIZE(fqd_states)) -+ qm_fq_state_cnt[(np.state & QM_MCR_NP_STATE_MASK)]++; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(fqd_states); i++) { -+ seq_printf(file, "%s count = %u\n", state_txt[i], -+ qm_fq_state_cnt[i]); -+ } -+ return 0; -+} -+ -+static int qman_fqd_prog_summary_show(struct seq_file *file, void *offset) -+{ -+ struct qm_fqd fqd; -+ struct qman_fq fq; -+ int ret, i , j; -+ u32 qm_prog_cnt[ARRAY_SIZE(mask_filter)/2]; -+ -+ memset(qm_prog_cnt, 0, sizeof(qm_prog_cnt)); -+ -+ for (i = 1; i < fqid_max; i++) { -+ memset(&fqd, 0, sizeof(struct qm_fqd)); -+ fq.fqid = i; -+ ret = qman_query_fq(&fq, &fqd); -+ if (ret) -+ return ret; -+ /* Keep a summary count of all states */ -+ for (j = 0; j < ARRAY_SIZE(mask_filter); j += 2) -+ if ((fqd.fq_ctrl & QM_FQCTRL_MASK) & -+ mask_filter[j].mask) -+ qm_prog_cnt[j/2]++; -+ } -+ for (i = 0; i < ARRAY_SIZE(mask_filter) / 2; i++) { -+ seq_printf(file, "%s count = %u\n", -+ get_fqd_ctrl_text(mask_filter[i*2].mask), -+ qm_prog_cnt[i]); -+ } -+ return 0; -+} -+ -+static int qman_fqd_summary_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ -+ /* Display summary of non programmable fields */ -+ ret = qman_fqd_non_prog_summary_show(file, offset); -+ if (ret) -+ return ret; -+ seq_puts(file, "-----------------------------------------\n"); -+ /* Display programmable fields */ -+ ret = qman_fqd_prog_summary_show(file, offset); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+static int qman_fqd_summary_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, qman_fqd_summary_show, NULL); -+} -+ -+static const struct file_operations qman_fqd_summary_fops = { -+ .owner = THIS_MODULE, -+ .open = qman_fqd_summary_open, -+ .read = seq_read, -+}; -+ -+/******************************************************************************* -+ * QMan destination work queue -+ ******************************************************************************/ -+struct qman_dest_wq_s { -+ u16 wq_id; -+}; -+static struct qman_dest_wq_s qman_dest_wq_data = { -+ .wq_id = 0, -+}; -+ -+static int qman_fqd_dest_wq_show(struct seq_file *file, void *offset) -+{ -+ struct qm_fqd fqd; -+ struct qman_fq fq; -+ int ret, i; -+ u16 *wq, wq_id = qman_dest_wq_data.wq_id; -+ struct line_buffer_fq line_buf; -+ -+ memset(&line_buf, 0, sizeof(line_buf)); -+ /* use vmalloc : need to allocate large memory region and don't -+ * require the memory to be physically contiguous. */ -+ wq = vzalloc(sizeof(u16) * (0xFFFF+1)); -+ if (!wq) -+ return -ENOMEM; -+ -+ seq_printf(file, "List of fq ids with destination work queue id" -+ " = 0x%x\n", wq_id); -+ -+ for (i = 1; i < fqid_max; i++) { -+ fq.fqid = i; -+ memset(&fqd, 0, sizeof(struct qm_fqd)); -+ ret = qman_query_fq(&fq, &fqd); -+ if (ret) { -+ vfree(wq); -+ return ret; -+ } -+ if (wq_id == fqd.dest_wq) -+ add_to_line_buffer(&line_buf, fq.fqid, file); -+ wq[fqd.dest_wq]++; -+ } -+ flush_line_buffer(&line_buf, file); -+ -+ seq_puts(file, "Summary of all FQD destination work queue values\n"); -+ for (i = 0; i < 0xFFFF; i++) { -+ if (wq[i]) -+ seq_printf(file, "Channel: 0x%x WQ: 0x%x WQ_ID: 0x%x, " -+ "count = %u\n", i >> 3, i & 0x3, i, wq[i]); -+ } -+ vfree(wq); -+ return 0; -+} -+ -+static ssize_t qman_fqd_dest_wq_write(struct file *f, const char __user *buf, -+ size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ if (val > 0xFFFF) -+ return -EINVAL; -+ qman_dest_wq_data.wq_id = val; -+ return count; -+} -+ -+static int qman_fqd_dest_wq_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, qman_fqd_dest_wq_show, NULL); -+} -+ -+static const struct file_operations qman_fqd_dest_wq_fops = { -+ .owner = THIS_MODULE, -+ .open = qman_fqd_dest_wq_open, -+ .read = seq_read, -+ .write = qman_fqd_dest_wq_write, -+}; -+ -+/******************************************************************************* -+ * QMan Intra-Class Scheduling Credit -+ ******************************************************************************/ -+static int qman_fqd_cred_show(struct seq_file *file, void *offset) -+{ -+ struct qm_fqd fqd; -+ struct qman_fq fq; -+ int ret, i; -+ u32 fq_cnt = 0; -+ struct line_buffer_fq line_buf; -+ -+ memset(&line_buf, 0, sizeof(line_buf)); -+ seq_puts(file, "List of fq ids with Intra-Class Scheduling Credit > 0" -+ "\n"); -+ -+ for (i = 1; i < fqid_max; i++) { -+ fq.fqid = i; -+ memset(&fqd, 0, sizeof(struct qm_fqd)); -+ ret = qman_query_fq(&fq, &fqd); -+ if (ret) -+ return ret; -+ if (fqd.ics_cred > 0) { -+ add_to_line_buffer(&line_buf, fq.fqid, file); -+ fq_cnt++; -+ } -+ } -+ flush_line_buffer(&line_buf, file); -+ -+ seq_printf(file, "Total FQD with ics_cred > 0 = %d\n", fq_cnt); -+ return 0; -+} -+ -+static int qman_fqd_cred_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, qman_fqd_cred_show, NULL); -+} -+ -+static const struct file_operations qman_fqd_cred_fops = { -+ .owner = THIS_MODULE, -+ .open = qman_fqd_cred_open, -+ .read = seq_read, -+}; -+ -+/******************************************************************************* -+ * Class Queue Fields -+ ******************************************************************************/ -+struct query_cq_fields_data_s { -+ u32 cqid; -+}; -+ -+static struct query_cq_fields_data_s query_cq_fields_data = { -+ .cqid = 1, -+}; -+ -+static int query_cq_fields_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ struct qm_mcr_ceetm_cq_query query_result; -+ unsigned int cqid; -+ unsigned int portal; -+ -+ if ((qman_ip_rev & 0xFF00) < QMAN_REV30) -+ return -EINVAL; -+ -+ cqid = query_cq_fields_data.cqid & 0x00FFFFFF; -+ portal = query_cq_fields_data.cqid >> 24; -+ if (portal > qm_dc_portal_fman1) -+ return -EINVAL; -+ -+ ret = qman_ceetm_query_cq(cqid, portal, &query_result); -+ if (ret) -+ return ret; -+ seq_printf(file, "Query CQ Fields Result cqid 0x%x on DCP %d\n", -+ cqid, portal); -+ seq_printf(file, " ccgid: %u\n", query_result.ccgid); -+ seq_printf(file, " state: %u\n", query_result.state); -+ seq_printf(file, " pfdr_hptr: %u\n", query_result.pfdr_hptr); -+ seq_printf(file, " pfdr_tptr: %u\n", query_result.pfdr_tptr); -+ seq_printf(file, " od1_xsfdr: %u\n", query_result.od1_xsfdr); -+ seq_printf(file, " od2_xsfdr: %u\n", query_result.od2_xsfdr); -+ seq_printf(file, " od3_xsfdr: %u\n", query_result.od3_xsfdr); -+ seq_printf(file, " od4_xsfdr: %u\n", query_result.od4_xsfdr); -+ seq_printf(file, " od5_xsfdr: %u\n", query_result.od5_xsfdr); -+ seq_printf(file, " od6_xsfdr: %u\n", query_result.od6_xsfdr); -+ seq_printf(file, " ra1_xsfdr: %u\n", query_result.ra1_xsfdr); -+ seq_printf(file, " ra2_xsfdr: %u\n", query_result.ra2_xsfdr); -+ seq_printf(file, " frame_count: %u\n", query_result.frm_cnt); -+ -+ return 0; -+} -+ -+static int query_cq_fields_open(struct inode *inode, -+ struct file *file) -+{ -+ return single_open(file, query_cq_fields_show, NULL); -+} -+ -+static ssize_t query_cq_fields_write(struct file *f, -+ const char __user *buf, size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ query_cq_fields_data.cqid = (u32)val; -+ return count; -+} -+ -+static const struct file_operations query_cq_fields_fops = { -+ .owner = THIS_MODULE, -+ .open = query_cq_fields_open, -+ .read = seq_read, -+ .write = query_cq_fields_write, -+ .release = single_release, -+}; -+ -+/******************************************************************************* -+ * READ CEETM_XSFDR_IN_USE -+ ******************************************************************************/ -+struct query_ceetm_xsfdr_data_s { -+ enum qm_dc_portal dcp_portal; -+}; -+ -+static struct query_ceetm_xsfdr_data_s query_ceetm_xsfdr_data; -+ -+static int query_ceetm_xsfdr_show(struct seq_file *file, void *offset) -+{ -+ int ret; -+ unsigned int xsfdr_in_use; -+ enum qm_dc_portal portal; -+ -+ -+ if (qman_ip_rev < QMAN_REV31) -+ return -EINVAL; -+ -+ portal = query_ceetm_xsfdr_data.dcp_portal; -+ ret = qman_ceetm_get_xsfdr(portal, &xsfdr_in_use); -+ if (ret) { -+ seq_printf(file, "Read CEETM_XSFDR_IN_USE on DCP %d failed\n", -+ portal); -+ return ret; -+ } -+ -+ seq_printf(file, "DCP%d: CEETM_XSFDR_IN_USE number is %u\n", portal, -+ (xsfdr_in_use & 0x1FFF)); -+ return 0; -+} -+ -+static int query_ceetm_xsfdr_open(struct inode *inode, -+ struct file *file) -+{ -+ return single_open(file, query_ceetm_xsfdr_show, NULL); -+} -+ -+static ssize_t query_ceetm_xsfdr_write(struct file *f, -+ const char __user *buf, size_t count, loff_t *off) -+{ -+ int ret; -+ unsigned long val; -+ -+ ret = user_input_convert(buf, count, &val); -+ if (ret) -+ return ret; -+ if (val > qm_dc_portal_fman1) -+ return -EINVAL; -+ query_ceetm_xsfdr_data.dcp_portal = (u32)val; -+ return count; -+} -+ -+static const struct file_operations query_ceetm_xsfdr_fops = { -+ .owner = THIS_MODULE, -+ .open = query_ceetm_xsfdr_open, -+ .read = seq_read, -+ .write = query_ceetm_xsfdr_write, -+ .release = single_release, -+}; -+ -+/* helper macros used in qman_debugfs_module_init */ -+#define QMAN_DBGFS_ENTRY(name, mode, parent, data, fops) \ -+ do { \ -+ d = debugfs_create_file(name, \ -+ mode, parent, \ -+ data, \ -+ fops); \ -+ if (d == NULL) { \ -+ ret = -ENOMEM; \ -+ goto _return; \ -+ } \ -+ } while (0) -+ -+/* dfs_root as parent */ -+#define QMAN_DBGFS_ENTRY_ROOT(name, mode, data, fops) \ -+ QMAN_DBGFS_ENTRY(name, mode, dfs_root, data, fops) -+ -+/* fqd_root as parent */ -+#define QMAN_DBGFS_ENTRY_FQDROOT(name, mode, data, fops) \ -+ QMAN_DBGFS_ENTRY(name, mode, fqd_root, data, fops) -+ -+/* fqd state */ -+#define QMAN_DBGFS_ENTRY_FQDSTATE(name, index) \ -+ QMAN_DBGFS_ENTRY_FQDROOT(name, S_IRUGO, \ -+ (void *)&mask_filter[index], &qman_fqd_ctrl_fops) -+ -+static int __init qman_debugfs_module_init(void) -+{ -+ int ret = 0; -+ struct dentry *d, *fqd_root; -+ u32 reg; -+ -+ fqid_max = 0; -+ init_ccsrmempeek(); -+ if (qman_ccsr_start) { -+ if (!qman_ccsrmempeek(®, QM_FQD_AR)) { -+ /* extract the size of the FQD window */ -+ reg = reg & 0x3f; -+ /* calculate valid frame queue descriptor range */ -+ fqid_max = (1 << (reg + 1)) / QM_FQD_BLOCK_SIZE; -+ } -+ } -+ dfs_root = debugfs_create_dir("qman", NULL); -+ fqd_root = debugfs_create_dir("fqd", dfs_root); -+ if (dfs_root == NULL || fqd_root == NULL) { -+ ret = -ENOMEM; -+ pr_err("Cannot create qman/fqd debugfs dir\n"); -+ goto _return; -+ } -+ if (fqid_max) { -+ QMAN_DBGFS_ENTRY_ROOT("ccsrmempeek", S_IRUGO | S_IWUGO, -+ NULL, &qman_ccsrmempeek_fops); -+ } -+ QMAN_DBGFS_ENTRY_ROOT("query_fq_np_fields", S_IRUGO | S_IWUGO, -+ &query_fq_np_fields_data, &query_fq_np_fields_fops); -+ -+ QMAN_DBGFS_ENTRY_ROOT("query_fq_fields", S_IRUGO | S_IWUGO, -+ &query_fq_fields_data, &query_fq_fields_fops); -+ -+ QMAN_DBGFS_ENTRY_ROOT("query_wq_lengths", S_IRUGO | S_IWUGO, -+ &query_wq_lengths_data, &query_wq_lengths_fops); -+ -+ QMAN_DBGFS_ENTRY_ROOT("query_cgr", S_IRUGO | S_IWUGO, -+ &query_cgr_data, &query_cgr_fops); -+ -+ QMAN_DBGFS_ENTRY_ROOT("query_congestion", S_IRUGO, -+ NULL, &query_congestion_fops); -+ -+ QMAN_DBGFS_ENTRY_ROOT("testwrite_cgr", S_IRUGO, -+ NULL, &testwrite_cgr_fops); -+ -+ QMAN_DBGFS_ENTRY_ROOT("testwrite_cgr_cgrid", S_IRUGO | S_IWUGO, -+ NULL, &teswrite_cgr_cgrid_fops); -+ -+ QMAN_DBGFS_ENTRY_ROOT("testwrite_cgr_ibcnt", S_IRUGO | S_IWUGO, -+ NULL, &teswrite_cgr_ibcnt_fops); -+ -+ QMAN_DBGFS_ENTRY_ROOT("query_ceetm_ccgr", S_IRUGO | S_IWUGO, -+ &query_ccgr_data, &query_ccgr_fops); -+ /* Create files with fqd_root as parent */ -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("stateoos", S_IRUGO, -+ (void *)&fqd_states[QM_MCR_NP_STATE_OOS], &qman_fqd_state_fops); -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("state_retired", S_IRUGO, -+ (void *)&fqd_states[QM_MCR_NP_STATE_RETIRED], -+ &qman_fqd_state_fops); -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("state_tentatively_sched", S_IRUGO, -+ (void *)&fqd_states[QM_MCR_NP_STATE_TEN_SCHED], -+ &qman_fqd_state_fops); -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("state_truly_sched", S_IRUGO, -+ (void *)&fqd_states[QM_MCR_NP_STATE_TRU_SCHED], -+ &qman_fqd_state_fops); -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("state_parked", S_IRUGO, -+ (void *)&fqd_states[QM_MCR_NP_STATE_PARKED], -+ &qman_fqd_state_fops); -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("state_active", S_IRUGO, -+ (void *)&fqd_states[QM_MCR_NP_STATE_ACTIVE], -+ &qman_fqd_state_fops); -+ QMAN_DBGFS_ENTRY_ROOT("query_cq_fields", S_IRUGO | S_IWUGO, -+ &query_cq_fields_data, &query_cq_fields_fops); -+ QMAN_DBGFS_ENTRY_ROOT("query_ceetm_xsfdr_in_use", S_IRUGO | S_IWUGO, -+ &query_ceetm_xsfdr_data, &query_ceetm_xsfdr_fops); -+ -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("cge_enable", 17); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("cge_disable", 16); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("tde_enable", 15); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("tde_disable", 14); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("orp_enable", 13); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("orp_disable", 12); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("ctx_a_stashing_enable", 11); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("ctx_a_stashing_disable", 10); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("cpc_enable", 9); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("cpc_disable", 8); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("sfdr_enable", 7); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("sfdr_disable", 6); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("avoid_blocking_enable", 5); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("avoid_blocking_disable", 4); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("hold_active_enable", 3); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("hold_active_disable", 2); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("prefer_in_cache_enable", 1); -+ -+ QMAN_DBGFS_ENTRY_FQDSTATE("prefer_in_cache_disable", 0); -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("summary", S_IRUGO, -+ NULL, &qman_fqd_summary_fops); -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("wq", S_IRUGO | S_IWUGO, -+ NULL, &qman_fqd_dest_wq_fops); -+ -+ QMAN_DBGFS_ENTRY_FQDROOT("cred", S_IRUGO, -+ NULL, &qman_fqd_cred_fops); -+ -+ return 0; -+ -+_return: -+ debugfs_remove_recursive(dfs_root); -+ return ret; -+} -+ -+static void __exit qman_debugfs_module_exit(void) -+{ -+ debugfs_remove_recursive(dfs_root); -+} -+ -+module_init(qman_debugfs_module_init); -+module_exit(qman_debugfs_module_exit); -+MODULE_LICENSE("Dual BSD/GPL"); ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_driver.c -@@ -0,0 +1,961 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "qman_private.h" -+ -+#include <asm/smp.h> /* hard_smp_processor_id() if !CONFIG_SMP */ -+#ifdef CONFIG_HOTPLUG_CPU -+#include <linux/cpu.h> -+#endif -+ -+/* Global variable containing revision id (even on non-control plane systems -+ * where CCSR isn't available) */ -+u16 qman_ip_rev; -+EXPORT_SYMBOL(qman_ip_rev); -+u8 qman_ip_cfg; -+EXPORT_SYMBOL(qman_ip_cfg); -+u16 qm_channel_pool1 = QMAN_CHANNEL_POOL1; -+EXPORT_SYMBOL(qm_channel_pool1); -+u16 qm_channel_caam = QMAN_CHANNEL_CAAM; -+EXPORT_SYMBOL(qm_channel_caam); -+u16 qm_channel_pme = QMAN_CHANNEL_PME; -+EXPORT_SYMBOL(qm_channel_pme); -+u16 qm_channel_dce = QMAN_CHANNEL_DCE; -+EXPORT_SYMBOL(qm_channel_dce); -+u16 qman_portal_max; -+EXPORT_SYMBOL(qman_portal_max); -+ -+u32 qman_clk; -+struct qm_ceetm qman_ceetms[QMAN_CEETM_MAX]; -+/* the qman ceetm instances on the given SoC */ -+u8 num_ceetms; -+ -+/* For these variables, and the portal-initialisation logic, the -+ * comments in bman_driver.c apply here so won't be repeated. */ -+static struct qman_portal *shared_portals[NR_CPUS]; -+static int num_shared_portals; -+static int shared_portals_idx; -+static LIST_HEAD(unused_pcfgs); -+static DEFINE_SPINLOCK(unused_pcfgs_lock); -+ -+/* A SDQCR mask comprising all the available/visible pool channels */ -+static u32 pools_sdqcr; -+ -+#define STR_ERR_NOPROP "No '%s' property in node %s\n" -+#define STR_ERR_CELL "'%s' is not a %d-cell range in node %s\n" -+#define STR_FQID_RANGE "fsl,fqid-range" -+#define STR_POOL_CHAN_RANGE "fsl,pool-channel-range" -+#define STR_CGRID_RANGE "fsl,cgrid-range" -+ -+/* A "fsl,fqid-range" node; release the given range to the allocator */ -+static __init int fsl_fqid_range_init(struct device_node *node) -+{ -+ int ret; -+ const u32 *range = of_get_property(node, STR_FQID_RANGE, &ret); -+ if (!range) { -+ pr_err(STR_ERR_NOPROP, STR_FQID_RANGE, node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err(STR_ERR_CELL, STR_FQID_RANGE, 2, node->full_name); -+ return -EINVAL; -+ } -+ qman_seed_fqid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ pr_info("Qman: FQID allocator includes range %d:%d\n", -+ be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ return 0; -+} -+ -+/* A "fsl,pool-channel-range" node; add to the SDQCR mask only */ -+static __init int fsl_pool_channel_range_sdqcr(struct device_node *node) -+{ -+ int ret; -+ const u32 *chanid = of_get_property(node, STR_POOL_CHAN_RANGE, &ret); -+ if (!chanid) { -+ pr_err(STR_ERR_NOPROP, STR_POOL_CHAN_RANGE, node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err(STR_ERR_CELL, STR_POOL_CHAN_RANGE, 1, node->full_name); -+ return -EINVAL; -+ } -+ for (ret = 0; ret < be32_to_cpu(chanid[1]); ret++) -+ pools_sdqcr |= QM_SDQCR_CHANNELS_POOL_CONV(be32_to_cpu(chanid[0]) + ret); -+ return 0; -+} -+ -+/* A "fsl,pool-channel-range" node; release the given range to the allocator */ -+static __init int fsl_pool_channel_range_init(struct device_node *node) -+{ -+ int ret; -+ const u32 *chanid = of_get_property(node, STR_POOL_CHAN_RANGE, &ret); -+ if (!chanid) { -+ pr_err(STR_ERR_NOPROP, STR_POOL_CHAN_RANGE, node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err(STR_ERR_CELL, STR_POOL_CHAN_RANGE, 1, node->full_name); -+ return -EINVAL; -+ } -+ qman_seed_pool_range(be32_to_cpu(chanid[0]), be32_to_cpu(chanid[1])); -+ pr_info("Qman: pool channel allocator includes range %d:%d\n", -+ be32_to_cpu(chanid[0]), be32_to_cpu(chanid[1])); -+ return 0; -+} -+ -+/* A "fsl,cgrid-range" node; release the given range to the allocator */ -+static __init int fsl_cgrid_range_init(struct device_node *node) -+{ -+ struct qman_cgr cgr; -+ int ret, errors = 0; -+ const u32 *range = of_get_property(node, STR_CGRID_RANGE, &ret); -+ if (!range) { -+ pr_err(STR_ERR_NOPROP, STR_CGRID_RANGE, node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err(STR_ERR_CELL, STR_CGRID_RANGE, 2, node->full_name); -+ return -EINVAL; -+ } -+ qman_seed_cgrid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ pr_info("Qman: CGRID allocator includes range %d:%d\n", -+ be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ for (cgr.cgrid = 0; cgr.cgrid < __CGR_NUM; cgr.cgrid++) { -+ ret = qman_modify_cgr(&cgr, QMAN_CGR_FLAG_USE_INIT, NULL); -+ if (ret) -+ errors++; -+ } -+ if (errors) -+ pr_err("Warning: %d error%s while initialising CGRs %d:%d\n", -+ errors, (errors > 1) ? "s" : "", range[0], range[1]); -+ return 0; -+} -+ -+static __init int fsl_ceetm_init(struct device_node *node) -+{ -+ enum qm_dc_portal dcp_portal; -+ struct qm_ceetm_sp *sp; -+ struct qm_ceetm_lni *lni; -+ int ret, i; -+ const u32 *range; -+ -+ /* Find LFQID range */ -+ range = of_get_property(node, "fsl,ceetm-lfqid-range", &ret); -+ if (!range) { -+ pr_err("No fsl,ceetm-lfqid-range in node %s\n", -+ node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err("fsl,ceetm-lfqid-range is not a 2-cell range in node" -+ " %s\n", node->full_name); -+ return -EINVAL; -+ } -+ -+ dcp_portal = (be32_to_cpu(range[0]) & 0x0F0000) >> 16; -+ if (dcp_portal > qm_dc_portal_fman1) { -+ pr_err("The DCP portal %d doesn't support CEETM\n", dcp_portal); -+ return -EINVAL; -+ } -+ -+ if (dcp_portal == qm_dc_portal_fman0) -+ qman_seed_ceetm0_lfqid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ if (dcp_portal == qm_dc_portal_fman1) -+ qman_seed_ceetm1_lfqid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ pr_debug("Qman: The lfqid allocator of CEETM %d includes range" -+ " 0x%x:0x%x\n", dcp_portal, be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ -+ qman_ceetms[dcp_portal].idx = dcp_portal; -+ INIT_LIST_HEAD(&qman_ceetms[dcp_portal].sub_portals); -+ INIT_LIST_HEAD(&qman_ceetms[dcp_portal].lnis); -+ -+ /* Find Sub-portal range */ -+ range = of_get_property(node, "fsl,ceetm-sp-range", &ret); -+ if (!range) { -+ pr_err("No fsl,ceetm-sp-range in node %s\n", node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err("fsl,ceetm-sp-range is not a 2-cell range in node %s\n", -+ node->full_name); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < be32_to_cpu(range[1]); i++) { -+ sp = kzalloc(sizeof(*sp), GFP_KERNEL); -+ if (!sp) { -+ pr_err("Can't alloc memory for sub-portal %d\n", -+ range[0] + i); -+ return -ENOMEM; -+ } -+ sp->idx = be32_to_cpu(range[0]) + i; -+ sp->dcp_idx = dcp_portal; -+ sp->is_claimed = 0; -+ list_add_tail(&sp->node, &qman_ceetms[dcp_portal].sub_portals); -+ sp++; -+ } -+ pr_debug("Qman: Reserve sub-portal %d:%d for CEETM %d\n", -+ be32_to_cpu(range[0]), be32_to_cpu(range[1]), dcp_portal); -+ qman_ceetms[dcp_portal].sp_range[0] = be32_to_cpu(range[0]); -+ qman_ceetms[dcp_portal].sp_range[1] = be32_to_cpu(range[1]); -+ -+ /* Find LNI range */ -+ range = of_get_property(node, "fsl,ceetm-lni-range", &ret); -+ if (!range) { -+ pr_err("No fsl,ceetm-lni-range in node %s\n", node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err("fsl,ceetm-lni-range is not a 2-cell range in node %s\n", -+ node->full_name); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < be32_to_cpu(range[1]); i++) { -+ lni = kzalloc(sizeof(*lni), GFP_KERNEL); -+ if (!lni) { -+ pr_err("Can't alloc memory for LNI %d\n", -+ range[0] + i); -+ return -ENOMEM; -+ } -+ lni->idx = be32_to_cpu(range[0]) + i; -+ lni->dcp_idx = dcp_portal; -+ lni->is_claimed = 0; -+ INIT_LIST_HEAD(&lni->channels); -+ list_add_tail(&lni->node, &qman_ceetms[dcp_portal].lnis); -+ lni++; -+ } -+ pr_debug("Qman: Reserve LNI %d:%d for CEETM %d\n", -+ be32_to_cpu(range[0]), be32_to_cpu(range[1]), dcp_portal); -+ qman_ceetms[dcp_portal].lni_range[0] = be32_to_cpu(range[0]); -+ qman_ceetms[dcp_portal].lni_range[1] = be32_to_cpu(range[1]); -+ -+ /* Find CEETM channel range */ -+ range = of_get_property(node, "fsl,ceetm-channel-range", &ret); -+ if (!range) { -+ pr_err("No fsl,ceetm-channel-range in node %s\n", -+ node->full_name); -+ return -EINVAL; -+ } -+ if (ret != 8) { -+ pr_err("fsl,ceetm-channel-range is not a 2-cell range in node" -+ "%s\n", node->full_name); -+ return -EINVAL; -+ } -+ -+ if (dcp_portal == qm_dc_portal_fman0) -+ qman_seed_ceetm0_channel_range(be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ if (dcp_portal == qm_dc_portal_fman1) -+ qman_seed_ceetm1_channel_range(be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ pr_debug("Qman: The channel allocator of CEETM %d includes" -+ " range %d:%d\n", dcp_portal, be32_to_cpu(range[0]), be32_to_cpu(range[1])); -+ -+ /* Set CEETM PRES register */ -+ ret = qman_ceetm_set_prescaler(dcp_portal); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+static void qman_get_ip_revision(struct device_node *dn) -+{ -+ u16 ip_rev = 0; -+ u8 ip_cfg = QMAN_REV_CFG_0; -+ for_each_compatible_node(dn, NULL, "fsl,qman-portal") { -+ if (!of_device_is_available(dn)) -+ continue; -+ if (of_device_is_compatible(dn, "fsl,qman-portal-1.0") || -+ of_device_is_compatible(dn, "fsl,qman-portal-1.0.0")) { -+ pr_err("QMAN rev1.0 on P4080 rev1 is not supported!\n"); -+ BUG_ON(1); -+ } else if (of_device_is_compatible(dn, "fsl,qman-portal-1.1") || -+ of_device_is_compatible(dn, "fsl,qman-portal-1.1.0")) { -+ ip_rev = QMAN_REV11; -+ qman_portal_max = 10; -+ } else if (of_device_is_compatible(dn, "fsl,qman-portal-1.2") || -+ of_device_is_compatible(dn, "fsl,qman-portal-1.2.0")) { -+ ip_rev = QMAN_REV12; -+ qman_portal_max = 10; -+ } else if (of_device_is_compatible(dn, "fsl,qman-portal-2.0") || -+ of_device_is_compatible(dn, "fsl,qman-portal-2.0.0")) { -+ ip_rev = QMAN_REV20; -+ qman_portal_max = 3; -+ } else if (of_device_is_compatible(dn, -+ "fsl,qman-portal-3.0.0")) { -+ ip_rev = QMAN_REV30; -+ qman_portal_max = 50; -+ } else if (of_device_is_compatible(dn, -+ "fsl,qman-portal-3.0.1")) { -+ ip_rev = QMAN_REV30; -+ qman_portal_max = 25; -+ ip_cfg = QMAN_REV_CFG_1; -+ } else if (of_device_is_compatible(dn, -+ "fsl,qman-portal-3.1.0")) { -+ ip_rev = QMAN_REV31; -+ qman_portal_max = 50; -+ } else if (of_device_is_compatible(dn, -+ "fsl,qman-portal-3.1.1")) { -+ ip_rev = QMAN_REV31; -+ qman_portal_max = 25; -+ ip_cfg = QMAN_REV_CFG_1; -+ } else if (of_device_is_compatible(dn, -+ "fsl,qman-portal-3.1.2")) { -+ ip_rev = QMAN_REV31; -+ qman_portal_max = 18; -+ ip_cfg = QMAN_REV_CFG_2; -+ } else if (of_device_is_compatible(dn, -+ "fsl,qman-portal-3.1.3")) { -+ ip_rev = QMAN_REV31; -+ qman_portal_max = 10; -+ ip_cfg = QMAN_REV_CFG_3; -+ } else if (of_device_is_compatible(dn, -+ "fsl,qman-portal-3.2.0")) { -+ ip_rev = QMAN_REV32; -+ qman_portal_max = 10; -+ ip_cfg = QMAN_REV_CFG_3; // TODO: Verify for ls1043 -+ } else if (of_device_is_compatible(dn, -+ "fsl,qman-portal-3.2.1")) { -+ ip_rev = QMAN_REV32; -+ qman_portal_max = 10; -+ ip_cfg = QMAN_REV_CFG_3; -+ } else { -+ pr_warn("unknown QMan version in portal node," -+ "default to rev1.1\n"); -+ ip_rev = QMAN_REV11; -+ qman_portal_max = 10; -+ } -+ -+ if (!qman_ip_rev) { -+ if (ip_rev) { -+ qman_ip_rev = ip_rev; -+ qman_ip_cfg = ip_cfg; -+ } else { -+ pr_warn("unknown Qman version," -+ " default to rev1.1\n"); -+ qman_ip_rev = QMAN_REV11; -+ qman_ip_cfg = QMAN_REV_CFG_0; -+ } -+ } else if (ip_rev && (qman_ip_rev != ip_rev)) -+ pr_warn("Revision=0x%04x, but portal '%s' has" -+ " 0x%04x\n", -+ qman_ip_rev, dn->full_name, ip_rev); -+ if (qman_ip_rev == ip_rev) -+ break; -+ } -+} -+ -+/* Parse a portal node, perform generic mapping duties and return the config. It -+ * is not known at this stage for what purpose (or even if) the portal will be -+ * used. */ -+static struct qm_portal_config * __init parse_pcfg(struct device_node *node) -+{ -+ struct qm_portal_config *pcfg; -+ const u32 *index_p; -+ u32 index, channel; -+ int irq, ret; -+ resource_size_t len; -+ -+ pcfg = kmalloc(sizeof(*pcfg), GFP_KERNEL); -+ if (!pcfg) { -+ pr_err("can't allocate portal config"); -+ return NULL; -+ } -+ -+ /* -+ * This is a *horrible hack*, but the IOMMU/PAMU driver needs a -+ * 'struct device' in order to get the PAMU stashing setup and the QMan -+ * portal [driver] won't function at all without ring stashing -+ * -+ * Making the QMan portal driver nice and proper is part of the -+ * upstreaming effort -+ */ -+ pcfg->dev.bus = &platform_bus_type; -+ pcfg->dev.of_node = node; -+#ifdef CONFIG_FSL_PAMU -+ pcfg->dev.archdata.iommu_domain = NULL; -+#endif -+ -+ ret = of_address_to_resource(node, DPA_PORTAL_CE, -+ &pcfg->addr_phys[DPA_PORTAL_CE]); -+ if (ret) { -+ pr_err("Can't get %s property '%s'\n", node->full_name, -+ "reg::CE"); -+ goto err; -+ } -+ ret = of_address_to_resource(node, DPA_PORTAL_CI, -+ &pcfg->addr_phys[DPA_PORTAL_CI]); -+ if (ret) { -+ pr_err("Can't get %s property '%s'\n", node->full_name, -+ "reg::CI"); -+ goto err; -+ } -+ index_p = of_get_property(node, "cell-index", &ret); -+ if (!index_p || (ret != 4)) { -+ pr_err("Can't get %s property '%s'\n", node->full_name, -+ "cell-index"); -+ goto err; -+ } -+ index = be32_to_cpu(*index_p); -+ if (index >= qman_portal_max) { -+ pr_err("QMan portal index %d is beyond max (%d)\n", -+ index, qman_portal_max); -+ goto err; -+ } -+ -+ channel = index + QM_CHANNEL_SWPORTAL0; -+ pcfg->public_cfg.channel = channel; -+ pcfg->public_cfg.cpu = -1; -+ irq = irq_of_parse_and_map(node, 0); -+ if (irq == 0) { -+ pr_err("Can't get %s property '%s'\n", node->full_name, -+ "interrupts"); -+ goto err; -+ } -+ pcfg->public_cfg.irq = irq; -+ pcfg->public_cfg.index = index; -+#ifdef CONFIG_FSL_QMAN_CONFIG -+ /* We need the same LIODN offset for all portals */ -+ qman_liodn_fixup(pcfg->public_cfg.channel); -+#endif -+ -+ len = resource_size(&pcfg->addr_phys[DPA_PORTAL_CE]); -+ if (len != (unsigned long)len) -+ goto err; -+ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_cache_ns( -+ pcfg->addr_phys[DPA_PORTAL_CE].start, -+ resource_size(&pcfg->addr_phys[DPA_PORTAL_CE])); -+ -+ pcfg->addr_virt[DPA_PORTAL_CI] = ioremap( -+ pcfg->addr_phys[DPA_PORTAL_CI].start, -+ resource_size(&pcfg->addr_phys[DPA_PORTAL_CI])); -+#else -+ pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_prot( -+ pcfg->addr_phys[DPA_PORTAL_CE].start, -+ (unsigned long)len, -+ 0); -+ pcfg->addr_virt[DPA_PORTAL_CI] = ioremap_prot( -+ pcfg->addr_phys[DPA_PORTAL_CI].start, -+ resource_size(&pcfg->addr_phys[DPA_PORTAL_CI]), -+ _PAGE_GUARDED | _PAGE_NO_CACHE); -+#endif -+ return pcfg; -+err: -+ kfree(pcfg); -+ return NULL; -+} -+ -+static struct qm_portal_config *get_pcfg(struct list_head *list) -+{ -+ struct qm_portal_config *pcfg; -+ if (list_empty(list)) -+ return NULL; -+ pcfg = list_entry(list->prev, struct qm_portal_config, list); -+ list_del(&pcfg->list); -+ return pcfg; -+} -+ -+static struct qm_portal_config *get_pcfg_idx(struct list_head *list, u32 idx) -+{ -+ struct qm_portal_config *pcfg; -+ if (list_empty(list)) -+ return NULL; -+ list_for_each_entry(pcfg, list, list) { -+ if (pcfg->public_cfg.index == idx) { -+ list_del(&pcfg->list); -+ return pcfg; -+ } -+ } -+ return NULL; -+} -+ -+static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu) -+{ -+#ifdef CONFIG_FSL_PAMU -+ int ret; -+ int window_count = 1; -+ struct iommu_domain_geometry geom_attr; -+ struct pamu_stash_attribute stash_attr; -+ -+ pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type); -+ if (!pcfg->iommu_domain) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_alloc() failed", -+ __func__); -+ goto _no_iommu; -+ } -+ geom_attr.aperture_start = 0; -+ geom_attr.aperture_end = -+ ((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1; -+ geom_attr.force_aperture = true; -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY, -+ &geom_attr); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS, -+ &window_count); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ stash_attr.cpu = cpu; -+ stash_attr.cache = PAMU_ATTR_CACHE_L1; -+ /* set stash information for the window */ -+ stash_attr.window = 0; -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, -+ DOMAIN_ATTR_FSL_PAMU_STASH, -+ &stash_attr); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36, -+ IOMMU_READ | IOMMU_WRITE); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_window_enable() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ ret = iommu_attach_device(pcfg->iommu_domain, &pcfg->dev); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_device_attach() = %d", -+ __func__, ret); -+ goto _iommu_domain_free; -+ } -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, -+ DOMAIN_ATTR_FSL_PAMU_ENABLE, -+ &window_count); -+ if (ret < 0) { -+ pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d", -+ __func__, ret); -+ goto _iommu_detach_device; -+ } -+ -+_no_iommu: -+#endif -+#ifdef CONFIG_FSL_QMAN_CONFIG -+ if (qman_set_sdest(pcfg->public_cfg.channel, cpu)) -+#endif -+ pr_warn("Failed to set QMan portal's stash request queue\n"); -+ -+ return; -+ -+#ifdef CONFIG_FSL_PAMU -+_iommu_detach_device: -+ iommu_detach_device(pcfg->iommu_domain, NULL); -+_iommu_domain_free: -+ iommu_domain_free(pcfg->iommu_domain); -+#endif -+} -+ -+struct qm_portal_config *qm_get_unused_portal_idx(u32 idx) -+{ -+ struct qm_portal_config *ret; -+ spin_lock(&unused_pcfgs_lock); -+ if (idx == QBMAN_ANY_PORTAL_IDX) -+ ret = get_pcfg(&unused_pcfgs); -+ else -+ ret = get_pcfg_idx(&unused_pcfgs, idx); -+ spin_unlock(&unused_pcfgs_lock); -+ /* Bind stashing LIODNs to the CPU we are currently executing on, and -+ * set the portal to use the stashing request queue corresonding to the -+ * cpu as well. The user-space driver assumption is that the pthread has -+ * to already be affine to one cpu only before opening a portal. If that -+ * check is circumvented, the only risk is a performance degradation - -+ * stashing will go to whatever cpu they happened to be running on when -+ * opening the device file, and if that isn't the cpu they subsequently -+ * bind to and do their polling on, tough. */ -+ if (ret) -+ portal_set_cpu(ret, hard_smp_processor_id()); -+ return ret; -+} -+ -+struct qm_portal_config *qm_get_unused_portal(void) -+{ -+ return qm_get_unused_portal_idx(QBMAN_ANY_PORTAL_IDX); -+} -+ -+void qm_put_unused_portal(struct qm_portal_config *pcfg) -+{ -+ spin_lock(&unused_pcfgs_lock); -+ list_add(&pcfg->list, &unused_pcfgs); -+ spin_unlock(&unused_pcfgs_lock); -+} -+ -+static struct qman_portal *init_pcfg(struct qm_portal_config *pcfg) -+{ -+ struct qman_portal *p; -+ -+ pcfg->iommu_domain = NULL; -+ portal_set_cpu(pcfg, pcfg->public_cfg.cpu); -+ p = qman_create_affine_portal(pcfg, NULL); -+ if (p) { -+ u32 irq_sources = 0; -+ /* Determine what should be interrupt-vs-poll driven */ -+#ifdef CONFIG_FSL_DPA_PIRQ_SLOW -+ irq_sources |= QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI | -+ QM_PIRQ_CSCI | QM_PIRQ_CCSCI; -+#endif -+#ifdef CONFIG_FSL_DPA_PIRQ_FAST -+ irq_sources |= QM_PIRQ_DQRI; -+#endif -+ qman_p_irqsource_add(p, irq_sources); -+ pr_info("Qman portal %sinitialised, cpu %d\n", -+ pcfg->public_cfg.is_shared ? "(shared) " : "", -+ pcfg->public_cfg.cpu); -+ } else -+ pr_crit("Qman portal failure on cpu %d\n", -+ pcfg->public_cfg.cpu); -+ return p; -+} -+ -+static void init_slave(int cpu) -+{ -+ struct qman_portal *p; -+ struct cpumask oldmask = current->cpus_allowed; -+ set_cpus_allowed_ptr(current, get_cpu_mask(cpu)); -+ p = qman_create_affine_slave(shared_portals[shared_portals_idx++], cpu); -+ if (!p) -+ pr_err("Qman slave portal failure on cpu %d\n", cpu); -+ else -+ pr_info("Qman portal %sinitialised, cpu %d\n", "(slave) ", cpu); -+ set_cpus_allowed_ptr(current, &oldmask); -+ if (shared_portals_idx >= num_shared_portals) -+ shared_portals_idx = 0; -+} -+ -+static struct cpumask want_unshared __initdata; -+static struct cpumask want_shared __initdata; -+ -+static int __init parse_qportals(char *str) -+{ -+ return parse_portals_bootarg(str, &want_shared, &want_unshared, -+ "qportals"); -+} -+__setup("qportals=", parse_qportals); -+ -+static void qman_portal_update_sdest(const struct qm_portal_config *pcfg, -+ unsigned int cpu) -+{ -+#ifdef CONFIG_FSL_PAMU -+ struct pamu_stash_attribute stash_attr; -+ int ret; -+ -+ if (pcfg->iommu_domain) { -+ stash_attr.cpu = cpu; -+ stash_attr.cache = PAMU_ATTR_CACHE_L1; -+ /* set stash information for the window */ -+ stash_attr.window = 0; -+ ret = iommu_domain_set_attr(pcfg->iommu_domain, -+ DOMAIN_ATTR_FSL_PAMU_STASH, &stash_attr); -+ if (ret < 0) { -+ pr_err("Failed to update pamu stash setting\n"); -+ return; -+ } -+ } -+#endif -+#ifdef CONFIG_FSL_QMAN_CONFIG -+ if (qman_set_sdest(pcfg->public_cfg.channel, cpu)) -+ pr_warn("Failed to update portal's stash request queue\n"); -+#endif -+} -+ -+static int qman_offline_cpu(unsigned int cpu) -+{ -+ struct qman_portal *p; -+ const struct qm_portal_config *pcfg; -+ p = (struct qman_portal *)affine_portals[cpu]; -+ if (p) { -+ pcfg = qman_get_qm_portal_config(p); -+ if (pcfg) { -+ irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(0)); -+ qman_portal_update_sdest(pcfg, 0); -+ } -+ } -+ return 0; -+} -+ -+#ifdef CONFIG_HOTPLUG_CPU -+static int qman_online_cpu(unsigned int cpu) -+{ -+ struct qman_portal *p; -+ const struct qm_portal_config *pcfg; -+ p = (struct qman_portal *)affine_portals[cpu]; -+ if (p) { -+ pcfg = qman_get_qm_portal_config(p); -+ if (pcfg) { -+ irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(cpu)); -+ qman_portal_update_sdest(pcfg, cpu); -+ } -+ } -+ return 0; -+} -+ -+#endif /* CONFIG_HOTPLUG_CPU */ -+ -+__init int qman_init(void) -+{ -+ struct cpumask slave_cpus; -+ struct cpumask unshared_cpus = *cpu_none_mask; -+ struct cpumask shared_cpus = *cpu_none_mask; -+ LIST_HEAD(unshared_pcfgs); -+ LIST_HEAD(shared_pcfgs); -+ struct device_node *dn; -+ struct qm_portal_config *pcfg; -+ struct qman_portal *p; -+ int cpu, ret; -+ const u32 *clk; -+ struct cpumask offline_cpus; -+ -+ /* Initialise the Qman (CCSR) device */ -+ for_each_compatible_node(dn, NULL, "fsl,qman") { -+ if (!qman_init_ccsr(dn)) -+ pr_info("Qman err interrupt handler present\n"); -+ else -+ pr_err("Qman CCSR setup failed\n"); -+ -+ clk = of_get_property(dn, "clock-frequency", NULL); -+ if (!clk) -+ pr_warn("Can't find Qman clock frequency\n"); -+ else -+ qman_clk = be32_to_cpu(*clk); -+ } -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ /* Setup lookup table for FQ demux */ -+ ret = qman_setup_fq_lookup_table(get_qman_fqd_size()/64); -+ if (ret) -+ return ret; -+#endif -+ -+ /* Get qman ip revision */ -+ qman_get_ip_revision(dn); -+ if ((qman_ip_rev & 0xff00) >= QMAN_REV30) { -+ qm_channel_pool1 = QMAN_CHANNEL_POOL1_REV3; -+ qm_channel_caam = QMAN_CHANNEL_CAAM_REV3; -+ qm_channel_pme = QMAN_CHANNEL_PME_REV3; -+ } -+ -+ if ((qman_ip_rev == QMAN_REV31) && (qman_ip_cfg == QMAN_REV_CFG_2)) -+ qm_channel_dce = QMAN_CHANNEL_DCE_QMANREV312; -+ -+ /* -+ * Parse the ceetm node to get how many ceetm instances are supported -+ * on the current silicon. num_ceetms must be confirmed before portals -+ * are intiailized. -+ */ -+ num_ceetms = 0; -+ for_each_compatible_node(dn, NULL, "fsl,qman-ceetm") -+ num_ceetms++; -+ -+ /* Parse pool channels into the SDQCR mask. (Must happen before portals -+ * are initialised.) */ -+ for_each_compatible_node(dn, NULL, "fsl,pool-channel-range") { -+ ret = fsl_pool_channel_range_sdqcr(dn); -+ if (ret) -+ return ret; -+ } -+ -+ memset(affine_portals, 0, sizeof(void *) * num_possible_cpus()); -+ /* Initialise portals. See bman_driver.c for comments */ -+ for_each_compatible_node(dn, NULL, "fsl,qman-portal") { -+ if (!of_device_is_available(dn)) -+ continue; -+ pcfg = parse_pcfg(dn); -+ if (pcfg) { -+ pcfg->public_cfg.pools = pools_sdqcr; -+ list_add_tail(&pcfg->list, &unused_pcfgs); -+ } -+ } -+ for_each_possible_cpu(cpu) { -+ if (cpumask_test_cpu(cpu, &want_shared)) { -+ pcfg = get_pcfg(&unused_pcfgs); -+ if (!pcfg) -+ break; -+ pcfg->public_cfg.cpu = cpu; -+ list_add_tail(&pcfg->list, &shared_pcfgs); -+ cpumask_set_cpu(cpu, &shared_cpus); -+ } -+ if (cpumask_test_cpu(cpu, &want_unshared)) { -+ if (cpumask_test_cpu(cpu, &shared_cpus)) -+ continue; -+ pcfg = get_pcfg(&unused_pcfgs); -+ if (!pcfg) -+ break; -+ pcfg->public_cfg.cpu = cpu; -+ list_add_tail(&pcfg->list, &unshared_pcfgs); -+ cpumask_set_cpu(cpu, &unshared_cpus); -+ } -+ } -+ if (list_empty(&shared_pcfgs) && list_empty(&unshared_pcfgs)) { -+ for_each_online_cpu(cpu) { -+ pcfg = get_pcfg(&unused_pcfgs); -+ if (!pcfg) -+ break; -+ pcfg->public_cfg.cpu = cpu; -+ list_add_tail(&pcfg->list, &unshared_pcfgs); -+ cpumask_set_cpu(cpu, &unshared_cpus); -+ } -+ } -+ cpumask_andnot(&slave_cpus, cpu_possible_mask, &shared_cpus); -+ cpumask_andnot(&slave_cpus, &slave_cpus, &unshared_cpus); -+ if (cpumask_empty(&slave_cpus)) { -+ if (!list_empty(&shared_pcfgs)) { -+ cpumask_or(&unshared_cpus, &unshared_cpus, -+ &shared_cpus); -+ cpumask_clear(&shared_cpus); -+ list_splice_tail(&shared_pcfgs, &unshared_pcfgs); -+ INIT_LIST_HEAD(&shared_pcfgs); -+ } -+ } else { -+ if (list_empty(&shared_pcfgs)) { -+ pcfg = get_pcfg(&unshared_pcfgs); -+ if (!pcfg) { -+ pr_crit("No QMan portals available!\n"); -+ return 0; -+ } -+ cpumask_clear_cpu(pcfg->public_cfg.cpu, &unshared_cpus); -+ cpumask_set_cpu(pcfg->public_cfg.cpu, &shared_cpus); -+ list_add_tail(&pcfg->list, &shared_pcfgs); -+ } -+ } -+ list_for_each_entry(pcfg, &unshared_pcfgs, list) { -+ pcfg->public_cfg.is_shared = 0; -+ p = init_pcfg(pcfg); -+ if (!p) { -+ pr_crit("Unable to configure portals\n"); -+ return 0; -+ } -+ } -+ list_for_each_entry(pcfg, &shared_pcfgs, list) { -+ pcfg->public_cfg.is_shared = 1; -+ p = init_pcfg(pcfg); -+ if (p) -+ shared_portals[num_shared_portals++] = p; -+ } -+ if (!cpumask_empty(&slave_cpus)) -+ for_each_cpu(cpu, &slave_cpus) -+ init_slave(cpu); -+ pr_info("Qman portals initialised\n"); -+ cpumask_andnot(&offline_cpus, cpu_possible_mask, cpu_online_mask); -+ for_each_cpu(cpu, &offline_cpus) -+ qman_offline_cpu(cpu); -+#ifdef CONFIG_HOTPLUG_CPU -+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, -+ "soc/qman_portal:online", -+ qman_online_cpu, qman_offline_cpu); -+ if (ret < 0) { -+ pr_err("qman: failed to register hotplug callbacks.\n"); -+ return ret; -+ } -+#endif -+ return 0; -+} -+ -+__init int qman_resource_init(void) -+{ -+ struct device_node *dn; -+ int ret; -+ -+ /* Initialise FQID allocation ranges */ -+ for_each_compatible_node(dn, NULL, "fsl,fqid-range") { -+ ret = fsl_fqid_range_init(dn); -+ if (ret) -+ return ret; -+ } -+ /* Initialise CGRID allocation ranges */ -+ for_each_compatible_node(dn, NULL, "fsl,cgrid-range") { -+ ret = fsl_cgrid_range_init(dn); -+ if (ret) -+ return ret; -+ } -+ /* Parse pool channels into the allocator. (Must happen after portals -+ * are initialised.) */ -+ for_each_compatible_node(dn, NULL, "fsl,pool-channel-range") { -+ ret = fsl_pool_channel_range_init(dn); -+ if (ret) -+ return ret; -+ } -+ -+ /* Parse CEETM */ -+ for_each_compatible_node(dn, NULL, "fsl,qman-ceetm") { -+ ret = fsl_ceetm_init(dn); -+ if (ret) -+ return ret; -+ } -+ return 0; -+} -+ -+#ifdef CONFIG_SUSPEND -+void suspend_unused_qportal(void) -+{ -+ struct qm_portal_config *pcfg; -+ -+ if (list_empty(&unused_pcfgs)) -+ return; -+ -+ list_for_each_entry(pcfg, &unused_pcfgs, list) { -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Need to save qportal %d\n", pcfg->public_cfg.index); -+#endif -+ /* save isdr, disable all via isdr, clear isr */ -+ pcfg->saved_isdr = -+ __raw_readl(pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08); -+ __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] + -+ 0xe08); -+ __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] + -+ 0xe00); -+ } -+ return; -+} -+ -+void resume_unused_qportal(void) -+{ -+ struct qm_portal_config *pcfg; -+ -+ if (list_empty(&unused_pcfgs)) -+ return; -+ -+ list_for_each_entry(pcfg, &unused_pcfgs, list) { -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Need to resume qportal %d\n", pcfg->public_cfg.index); -+#endif -+ /* restore isdr */ -+ __raw_writel(pcfg->saved_isdr, -+ pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08); -+ } -+ return; -+} -+#endif ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_high.c -@@ -0,0 +1,5669 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "qman_low.h" -+ -+/* Compilation constants */ -+#define DQRR_MAXFILL 15 -+#define EQCR_ITHRESH 4 /* if EQCR congests, interrupt threshold */ -+#define IRQNAME "QMan portal %d" -+#define MAX_IRQNAME 16 /* big enough for "QMan portal %d" */ -+ -+/* Divide 'n' by 'd', rounding down if 'r' is negative, rounding up if it's -+ * positive, and rounding to the closest value if it's zero. NB, this macro -+ * implicitly upgrades parameters to unsigned 64-bit, so feed it with types -+ * that are compatible with this. NB, these arguments should not be expressions -+ * unless it is safe for them to be evaluated multiple times. Eg. do not pass -+ * in "some_value++" as a parameter to the macro! */ -+#define ROUNDING(n, d, r) \ -+ (((r) < 0) ? div64_u64((n), (d)) : \ -+ (((r) > 0) ? div64_u64(((n) + (d) - 1), (d)) : \ -+ div64_u64(((n) + ((d) / 2)), (d)))) -+ -+/* Lock/unlock frame queues, subject to the "LOCKED" flag. This is about -+ * inter-processor locking only. Note, FQLOCK() is always called either under a -+ * local_irq_save() or from interrupt context - hence there's no need for irq -+ * protection (and indeed, attempting to nest irq-protection doesn't work, as -+ * the "irq en/disable" machinery isn't recursive...). */ -+#define FQLOCK(fq) \ -+ do { \ -+ struct qman_fq *__fq478 = (fq); \ -+ if (fq_isset(__fq478, QMAN_FQ_FLAG_LOCKED)) \ -+ spin_lock(&__fq478->fqlock); \ -+ } while (0) -+#define FQUNLOCK(fq) \ -+ do { \ -+ struct qman_fq *__fq478 = (fq); \ -+ if (fq_isset(__fq478, QMAN_FQ_FLAG_LOCKED)) \ -+ spin_unlock(&__fq478->fqlock); \ -+ } while (0) -+ -+static inline void fq_set(struct qman_fq *fq, u32 mask) -+{ -+ set_bits(mask, &fq->flags); -+} -+static inline void fq_clear(struct qman_fq *fq, u32 mask) -+{ -+ clear_bits(mask, &fq->flags); -+} -+static inline int fq_isset(struct qman_fq *fq, u32 mask) -+{ -+ return fq->flags & mask; -+} -+static inline int fq_isclear(struct qman_fq *fq, u32 mask) -+{ -+ return !(fq->flags & mask); -+} -+ -+struct qman_portal { -+ struct qm_portal p; -+ unsigned long bits; /* PORTAL_BITS_*** - dynamic, strictly internal */ -+ unsigned long irq_sources; -+ u32 use_eqcr_ci_stashing; -+ u32 slowpoll; /* only used when interrupts are off */ -+ struct qman_fq *vdqcr_owned; /* only 1 volatile dequeue at a time */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ struct qman_fq *eqci_owned; /* only 1 enqueue WAIT_SYNC at a time */ -+#endif -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ raw_spinlock_t sharing_lock; /* only used if is_shared */ -+ int is_shared; -+ struct qman_portal *sharing_redirect; -+#endif -+ u32 sdqcr; -+ int dqrr_disable_ref; -+ /* A portal-specific handler for DCP ERNs. If this is NULL, the global -+ * handler is called instead. */ -+ qman_cb_dc_ern cb_dc_ern; -+ /* When the cpu-affine portal is activated, this is non-NULL */ -+ const struct qm_portal_config *config; -+ /* This is needed for providing a non-NULL device to dma_map_***() */ -+ struct platform_device *pdev; -+ struct dpa_rbtree retire_table; -+ char irqname[MAX_IRQNAME]; -+ /* 2-element array. cgrs[0] is mask, cgrs[1] is snapshot. */ -+ struct qman_cgrs *cgrs; -+ /* linked-list of CSCN handlers. */ -+ struct list_head cgr_cbs; -+ /* list lock */ -+ spinlock_t cgr_lock; -+ /* 2-element array. ccgrs[0] is mask, ccgrs[1] is snapshot. */ -+ struct qman_ccgrs *ccgrs[QMAN_CEETM_MAX]; -+ /* 256-element array, each is a linked-list of CCSCN handlers. */ -+ struct list_head ccgr_cbs[QMAN_CEETM_MAX]; -+ /* list lock */ -+ spinlock_t ccgr_lock; -+ /* track if memory was allocated by the driver */ -+ u8 alloced; -+ /* power management data */ -+ u32 save_isdr; -+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -+ /* Keep a shadow copy of the DQRR on LE systems as the SW needs to -+ * do byte swaps of DQRR read only memory. First entry must be aligned -+ * to 2 ** 10 to ensure DQRR index calculations based shadow copy -+ * address (6 bits for address shift + 4 bits for the DQRR size). -+ */ -+ struct qm_dqrr_entry shadow_dqrr[QM_DQRR_SIZE] __aligned(1024); -+#endif -+}; -+ -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+#define PORTAL_IRQ_LOCK(p, irqflags) \ -+ do { \ -+ if ((p)->is_shared) \ -+ raw_spin_lock_irqsave(&(p)->sharing_lock, irqflags); \ -+ else \ -+ local_irq_save(irqflags); \ -+ } while (0) -+#define PORTAL_IRQ_UNLOCK(p, irqflags) \ -+ do { \ -+ if ((p)->is_shared) \ -+ raw_spin_unlock_irqrestore(&(p)->sharing_lock, \ -+ irqflags); \ -+ else \ -+ local_irq_restore(irqflags); \ -+ } while (0) -+#else -+#define PORTAL_IRQ_LOCK(p, irqflags) local_irq_save(irqflags) -+#define PORTAL_IRQ_UNLOCK(p, irqflags) local_irq_restore(irqflags) -+#endif -+ -+/* Global handler for DCP ERNs. Used when the portal receiving the message does -+ * not have a portal-specific handler. */ -+static qman_cb_dc_ern cb_dc_ern; -+ -+static cpumask_t affine_mask; -+static DEFINE_SPINLOCK(affine_mask_lock); -+static u16 affine_channels[NR_CPUS]; -+static DEFINE_PER_CPU(struct qman_portal, qman_affine_portal); -+void *affine_portals[NR_CPUS]; -+ -+/* "raw" gets the cpu-local struct whether it's a redirect or not. */ -+static inline struct qman_portal *get_raw_affine_portal(void) -+{ -+ return &get_cpu_var(qman_affine_portal); -+} -+/* For ops that can redirect, this obtains the portal to use */ -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+static inline struct qman_portal *get_affine_portal(void) -+{ -+ struct qman_portal *p = get_raw_affine_portal(); -+ if (p->sharing_redirect) -+ return p->sharing_redirect; -+ return p; -+} -+#else -+#define get_affine_portal() get_raw_affine_portal() -+#endif -+/* For every "get", there must be a "put" */ -+static inline void put_affine_portal(void) -+{ -+ put_cpu_var(qman_affine_portal); -+} -+/* Exception: poll functions assume the caller is cpu-affine and in no risk of -+ * re-entrance, which are the two reasons we usually use the get/put_cpu_var() -+ * semantic - ie. to disable pre-emption. Some use-cases expect the execution -+ * context to remain as non-atomic during poll-triggered callbacks as it was -+ * when the poll API was first called (eg. NAPI), so we go out of our way in -+ * this case to not disable pre-emption. */ -+static inline struct qman_portal *get_poll_portal(void) -+{ -+ return &get_cpu_var(qman_affine_portal); -+} -+#define put_poll_portal() -+ -+/* This gives a FQID->FQ lookup to cover the fact that we can't directly demux -+ * retirement notifications (the fact they are sometimes h/w-consumed means that -+ * contextB isn't always a s/w demux - and as we can't know which case it is -+ * when looking at the notification, we have to use the slow lookup for all of -+ * them). NB, it's possible to have multiple FQ objects refer to the same FQID -+ * (though at most one of them should be the consumer), so this table isn't for -+ * all FQs - FQs are added when retirement commands are issued, and removed when -+ * they complete, which also massively reduces the size of this table. */ -+IMPLEMENT_DPA_RBTREE(fqtree, struct qman_fq, node, fqid); -+ -+/* This is what everything can wait on, even if it migrates to a different cpu -+ * to the one whose affine portal it is waiting on. */ -+static DECLARE_WAIT_QUEUE_HEAD(affine_queue); -+ -+static inline int table_push_fq(struct qman_portal *p, struct qman_fq *fq) -+{ -+ int ret = fqtree_push(&p->retire_table, fq); -+ if (ret) -+ pr_err("ERROR: double FQ-retirement %d\n", fq->fqid); -+ return ret; -+} -+ -+static inline void table_del_fq(struct qman_portal *p, struct qman_fq *fq) -+{ -+ fqtree_del(&p->retire_table, fq); -+} -+ -+static inline struct qman_fq *table_find_fq(struct qman_portal *p, u32 fqid) -+{ -+ return fqtree_find(&p->retire_table, fqid); -+} -+ -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+static void **qman_fq_lookup_table; -+static size_t qman_fq_lookup_table_size; -+ -+int qman_setup_fq_lookup_table(size_t num_entries) -+{ -+ num_entries++; -+ /* Allocate 1 more entry since the first entry is not used */ -+ qman_fq_lookup_table = vzalloc((num_entries * sizeof(void *))); -+ if (!qman_fq_lookup_table) { -+ pr_err("QMan: Could not allocate fq lookup table\n"); -+ return -ENOMEM; -+ } -+ qman_fq_lookup_table_size = num_entries; -+ pr_info("QMan: Allocated lookup table at %p, entry count %lu\n", -+ qman_fq_lookup_table, -+ (unsigned long)qman_fq_lookup_table_size); -+ return 0; -+} -+ -+/* global structure that maintains fq object mapping */ -+static DEFINE_SPINLOCK(fq_hash_table_lock); -+ -+static int find_empty_fq_table_entry(u32 *entry, struct qman_fq *fq) -+{ -+ u32 i; -+ -+ spin_lock(&fq_hash_table_lock); -+ /* Can't use index zero because this has special meaning -+ * in context_b field. */ -+ for (i = 1; i < qman_fq_lookup_table_size; i++) { -+ if (qman_fq_lookup_table[i] == NULL) { -+ *entry = i; -+ qman_fq_lookup_table[i] = fq; -+ spin_unlock(&fq_hash_table_lock); -+ return 0; -+ } -+ } -+ spin_unlock(&fq_hash_table_lock); -+ return -ENOMEM; -+} -+ -+static void clear_fq_table_entry(u32 entry) -+{ -+ spin_lock(&fq_hash_table_lock); -+ BUG_ON(entry >= qman_fq_lookup_table_size); -+ qman_fq_lookup_table[entry] = NULL; -+ spin_unlock(&fq_hash_table_lock); -+} -+ -+static inline struct qman_fq *get_fq_table_entry(u32 entry) -+{ -+ BUG_ON(entry >= qman_fq_lookup_table_size); -+ return qman_fq_lookup_table[entry]; -+} -+#endif -+ -+static inline void cpu_to_hw_fqd(struct qm_fqd *fqd) -+{ -+ /* Byteswap the FQD to HW format */ -+ fqd->fq_ctrl = cpu_to_be16(fqd->fq_ctrl); -+ fqd->dest_wq = cpu_to_be16(fqd->dest_wq); -+ fqd->ics_cred = cpu_to_be16(fqd->ics_cred); -+ fqd->context_b = cpu_to_be32(fqd->context_b); -+ fqd->context_a.opaque = cpu_to_be64(fqd->context_a.opaque); -+} -+ -+static inline void hw_fqd_to_cpu(struct qm_fqd *fqd) -+{ -+ /* Byteswap the FQD to CPU format */ -+ fqd->fq_ctrl = be16_to_cpu(fqd->fq_ctrl); -+ fqd->dest_wq = be16_to_cpu(fqd->dest_wq); -+ fqd->ics_cred = be16_to_cpu(fqd->ics_cred); -+ fqd->context_b = be32_to_cpu(fqd->context_b); -+ fqd->context_a.opaque = be64_to_cpu(fqd->context_a.opaque); -+} -+ -+/* Swap a 40 bit address */ -+static inline u64 cpu_to_be40(u64 in) -+{ -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ return in; -+#else -+ u64 out = 0; -+ u8 *p = (u8 *) &out; -+ p[0] = in >> 32; -+ p[1] = in >> 24; -+ p[2] = in >> 16; -+ p[3] = in >> 8; -+ p[4] = in >> 0; -+ return out; -+#endif -+} -+static inline u64 be40_to_cpu(u64 in) -+{ -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ return in; -+#else -+ u64 out = 0; -+ u8 *pout = (u8 *) &out; -+ u8 *pin = (u8 *) ∈ -+ pout[0] = pin[4]; -+ pout[1] = pin[3]; -+ pout[2] = pin[2]; -+ pout[3] = pin[1]; -+ pout[4] = pin[0]; -+ return out; -+#endif -+} -+ -+/* Swap a 24 bit value */ -+static inline u32 cpu_to_be24(u32 in) -+{ -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ return in; -+#else -+ u32 out = 0; -+ u8 *p = (u8 *) &out; -+ p[0] = in >> 16; -+ p[1] = in >> 8; -+ p[2] = in >> 0; -+ return out; -+#endif -+} -+ -+static inline u32 be24_to_cpu(u32 in) -+{ -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ return in; -+#else -+ u32 out = 0; -+ u8 *pout = (u8 *) &out; -+ u8 *pin = (u8 *) ∈ -+ pout[0] = pin[2]; -+ pout[1] = pin[1]; -+ pout[2] = pin[0]; -+ return out; -+#endif -+} -+ -+static inline u64 be48_to_cpu(u64 in) -+{ -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ return in; -+#else -+ u64 out = 0; -+ u8 *pout = (u8 *) &out; -+ u8 *pin = (u8 *) ∈ -+ -+ pout[0] = pin[5]; -+ pout[1] = pin[4]; -+ pout[2] = pin[3]; -+ pout[3] = pin[2]; -+ pout[4] = pin[1]; -+ pout[5] = pin[0]; -+ return out; -+#endif -+} -+static inline void cpu_to_hw_fd(struct qm_fd *fd) -+{ -+ fd->opaque_addr = cpu_to_be64(fd->opaque_addr); -+ fd->status = cpu_to_be32(fd->status); -+ fd->opaque = cpu_to_be32(fd->opaque); -+} -+ -+static inline void hw_fd_to_cpu(struct qm_fd *fd) -+{ -+ fd->opaque_addr = be64_to_cpu(fd->opaque_addr); -+ fd->status = be32_to_cpu(fd->status); -+ fd->opaque = be32_to_cpu(fd->opaque); -+} -+ -+static inline void hw_cq_query_to_cpu(struct qm_mcr_ceetm_cq_query *cq_query) -+{ -+ cq_query->ccgid = be16_to_cpu(cq_query->ccgid); -+ cq_query->state = be16_to_cpu(cq_query->state); -+ cq_query->pfdr_hptr = be24_to_cpu(cq_query->pfdr_hptr); -+ cq_query->pfdr_tptr = be24_to_cpu(cq_query->pfdr_tptr); -+ cq_query->od1_xsfdr = be16_to_cpu(cq_query->od1_xsfdr); -+ cq_query->od2_xsfdr = be16_to_cpu(cq_query->od2_xsfdr); -+ cq_query->od3_xsfdr = be16_to_cpu(cq_query->od3_xsfdr); -+ cq_query->od4_xsfdr = be16_to_cpu(cq_query->od4_xsfdr); -+ cq_query->od5_xsfdr = be16_to_cpu(cq_query->od5_xsfdr); -+ cq_query->od6_xsfdr = be16_to_cpu(cq_query->od6_xsfdr); -+ cq_query->ra1_xsfdr = be16_to_cpu(cq_query->ra1_xsfdr); -+ cq_query->ra2_xsfdr = be16_to_cpu(cq_query->ra2_xsfdr); -+ cq_query->frm_cnt = be24_to_cpu(cq_query->frm_cnt); -+} -+ -+static inline void hw_ccgr_query_to_cpu(struct qm_mcr_ceetm_ccgr_query *ccgr_q) -+{ -+ int i; -+ -+ ccgr_q->cm_query.cs_thres.hword = -+ be16_to_cpu(ccgr_q->cm_query.cs_thres.hword); -+ ccgr_q->cm_query.cs_thres_x.hword = -+ be16_to_cpu(ccgr_q->cm_query.cs_thres_x.hword); -+ ccgr_q->cm_query.td_thres.hword = -+ be16_to_cpu(ccgr_q->cm_query.td_thres.hword); -+ ccgr_q->cm_query.wr_parm_g.word = -+ be32_to_cpu(ccgr_q->cm_query.wr_parm_g.word); -+ ccgr_q->cm_query.wr_parm_y.word = -+ be32_to_cpu(ccgr_q->cm_query.wr_parm_y.word); -+ ccgr_q->cm_query.wr_parm_r.word = -+ be32_to_cpu(ccgr_q->cm_query.wr_parm_r.word); -+ ccgr_q->cm_query.cscn_targ_dcp = -+ be16_to_cpu(ccgr_q->cm_query.cscn_targ_dcp); -+ ccgr_q->cm_query.i_cnt = be40_to_cpu(ccgr_q->cm_query.i_cnt); -+ ccgr_q->cm_query.a_cnt = be40_to_cpu(ccgr_q->cm_query.a_cnt); -+ for (i = 0; i < ARRAY_SIZE(ccgr_q->cm_query.cscn_targ_swp); i++) -+ ccgr_q->cm_query.cscn_targ_swp[i] = -+ be32_to_cpu(ccgr_q->cm_query.cscn_targ_swp[i]); -+} -+ -+/* In the case that slow- and fast-path handling are both done by qman_poll() -+ * (ie. because there is no interrupt handling), we ought to balance how often -+ * we do the fast-path poll versus the slow-path poll. We'll use two decrementer -+ * sources, so we call the fast poll 'n' times before calling the slow poll -+ * once. The idle decrementer constant is used when the last slow-poll detected -+ * no work to do, and the busy decrementer constant when the last slow-poll had -+ * work to do. */ -+#define SLOW_POLL_IDLE 1000 -+#define SLOW_POLL_BUSY 10 -+static u32 __poll_portal_slow(struct qman_portal *p, u32 is); -+static inline unsigned int __poll_portal_fast(struct qman_portal *p, -+ unsigned int poll_limit); -+ -+/* Portal interrupt handler */ -+static irqreturn_t portal_isr(__always_unused int irq, void *ptr) -+{ -+ struct qman_portal *p = ptr; -+ /* -+ * The CSCI/CCSCI source is cleared inside __poll_portal_slow(), because -+ * it could race against a Query Congestion State command also given -+ * as part of the handling of this interrupt source. We mustn't -+ * clear it a second time in this top-level function. -+ */ -+ u32 clear = QM_DQAVAIL_MASK | (p->irq_sources & -+ ~(QM_PIRQ_CSCI | QM_PIRQ_CCSCI)); -+ u32 is = qm_isr_status_read(&p->p) & p->irq_sources; -+ /* DQRR-handling if it's interrupt-driven */ -+ if (is & QM_PIRQ_DQRI) -+ __poll_portal_fast(p, CONFIG_FSL_QMAN_POLL_LIMIT); -+ /* Handling of anything else that's interrupt-driven */ -+ clear |= __poll_portal_slow(p, is); -+ qm_isr_status_clear(&p->p, clear); -+ return IRQ_HANDLED; -+} -+ -+/* This inner version is used privately by qman_create_affine_portal(), as well -+ * as by the exported qman_stop_dequeues(). */ -+static inline void qman_stop_dequeues_ex(struct qman_portal *p) -+{ -+ unsigned long irqflags __maybe_unused; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ if (!(p->dqrr_disable_ref++)) -+ qm_dqrr_set_maxfill(&p->p, 0); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+} -+ -+static int drain_mr_fqrni(struct qm_portal *p) -+{ -+ const struct qm_mr_entry *msg; -+loop: -+ msg = qm_mr_current(p); -+ if (!msg) { -+ /* if MR was full and h/w had other FQRNI entries to produce, we -+ * need to allow it time to produce those entries once the -+ * existing entries are consumed. A worst-case situation -+ * (fully-loaded system) means h/w sequencers may have to do 3-4 -+ * other things before servicing the portal's MR pump, each of -+ * which (if slow) may take ~50 qman cycles (which is ~200 -+ * processor cycles). So rounding up and then multiplying this -+ * worst-case estimate by a factor of 10, just to be -+ * ultra-paranoid, goes as high as 10,000 cycles. NB, we consume -+ * one entry at a time, so h/w has an opportunity to produce new -+ * entries well before the ring has been fully consumed, so -+ * we're being *really* paranoid here. */ -+ u64 now, then = mfatb(); -+ do { -+ now = mfatb(); -+ } while ((then + 10000) > now); -+ msg = qm_mr_current(p); -+ if (!msg) -+ return 0; -+ } -+ if ((msg->verb & QM_MR_VERB_TYPE_MASK) != QM_MR_VERB_FQRNI) { -+ /* We aren't draining anything but FQRNIs */ -+ pr_err("QMan found verb 0x%x in MR\n", msg->verb); -+ return -1; -+ } -+ qm_mr_next(p); -+ qm_mr_cci_consume(p, 1); -+ goto loop; -+} -+ -+#ifdef CONFIG_SUSPEND -+static int _qman_portal_suspend_noirq(struct device *dev) -+{ -+ struct qman_portal *p = (struct qman_portal *)dev->platform_data; -+#ifdef CONFIG_PM_DEBUG -+ struct platform_device *pdev = to_platform_device(dev); -+#endif -+ -+ p->save_isdr = qm_isr_disable_read(&p->p); -+ qm_isr_disable_write(&p->p, 0xffffffff); -+ qm_isr_status_clear(&p->p, 0xffffffff); -+#ifdef CONFIG_PM_DEBUG -+ pr_info("Suspend for %s\n", pdev->name); -+#endif -+ return 0; -+} -+ -+static int _qman_portal_resume_noirq(struct device *dev) -+{ -+ struct qman_portal *p = (struct qman_portal *)dev->platform_data; -+ -+ /* restore isdr */ -+ qm_isr_disable_write(&p->p, p->save_isdr); -+ return 0; -+} -+#else -+#define _qman_portal_suspend_noirq NULL -+#define _qman_portal_resume_noirq NULL -+#endif -+ -+struct dev_pm_domain qman_portal_device_pm_domain = { -+ .ops = { -+ USE_PLATFORM_PM_SLEEP_OPS -+ .suspend_noirq = _qman_portal_suspend_noirq, -+ .resume_noirq = _qman_portal_resume_noirq, -+ } -+}; -+ -+struct qman_portal *qman_create_portal( -+ struct qman_portal *portal, -+ const struct qm_portal_config *config, -+ const struct qman_cgrs *cgrs) -+{ -+ struct qm_portal *__p; -+ char buf[16]; -+ int ret; -+ u32 isdr; -+ -+ if (!portal) { -+ portal = kmalloc(sizeof(*portal), GFP_KERNEL); -+ if (!portal) -+ return portal; -+ portal->alloced = 1; -+ } else -+ portal->alloced = 0; -+ -+ __p = &portal->p; -+ -+#if (defined CONFIG_PPC || defined CONFIG_PPC64) && defined CONFIG_FSL_PAMU -+ /* PAMU is required for stashing */ -+ portal->use_eqcr_ci_stashing = ((qman_ip_rev >= QMAN_REV30) ? -+ 1 : 0); -+#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ portal->use_eqcr_ci_stashing = 1; -+#else -+ portal->use_eqcr_ci_stashing = 0; -+#endif -+ -+ /* prep the low-level portal struct with the mapped addresses from the -+ * config, everything that follows depends on it and "config" is more -+ * for (de)reference... */ -+ __p->addr.addr_ce = config->addr_virt[DPA_PORTAL_CE]; -+ __p->addr.addr_ci = config->addr_virt[DPA_PORTAL_CI]; -+ /* -+ * If CI-stashing is used, the current defaults use a threshold of 3, -+ * and stash with high-than-DQRR priority. -+ */ -+ if (qm_eqcr_init(__p, qm_eqcr_pvb, -+ portal->use_eqcr_ci_stashing ? 3 : 0, 1)) { -+ pr_err("Qman EQCR initialisation failed\n"); -+ goto fail_eqcr; -+ } -+ if (qm_dqrr_init(__p, config, qm_dqrr_dpush, qm_dqrr_pvb, -+ qm_dqrr_cdc, DQRR_MAXFILL)) { -+ pr_err("Qman DQRR initialisation failed\n"); -+ goto fail_dqrr; -+ } -+ if (qm_mr_init(__p, qm_mr_pvb, qm_mr_cci)) { -+ pr_err("Qman MR initialisation failed\n"); -+ goto fail_mr; -+ } -+ if (qm_mc_init(__p)) { -+ pr_err("Qman MC initialisation failed\n"); -+ goto fail_mc; -+ } -+ if (qm_isr_init(__p)) { -+ pr_err("Qman ISR initialisation failed\n"); -+ goto fail_isr; -+ } -+ /* static interrupt-gating controls */ -+ qm_dqrr_set_ithresh(__p, CONFIG_FSL_QMAN_PIRQ_DQRR_ITHRESH); -+ qm_mr_set_ithresh(__p, CONFIG_FSL_QMAN_PIRQ_MR_ITHRESH); -+ qm_isr_set_iperiod(__p, CONFIG_FSL_QMAN_PIRQ_IPERIOD); -+ portal->cgrs = kmalloc(2 * sizeof(*cgrs), GFP_KERNEL); -+ if (!portal->cgrs) -+ goto fail_cgrs; -+ /* initial snapshot is no-depletion */ -+ qman_cgrs_init(&portal->cgrs[1]); -+ if (cgrs) -+ portal->cgrs[0] = *cgrs; -+ else -+ /* if the given mask is NULL, assume all CGRs can be seen */ -+ qman_cgrs_fill(&portal->cgrs[0]); -+ INIT_LIST_HEAD(&portal->cgr_cbs); -+ spin_lock_init(&portal->cgr_lock); -+ if (num_ceetms) { -+ for (ret = 0; ret < num_ceetms; ret++) { -+ portal->ccgrs[ret] = kmalloc(2 * -+ sizeof(struct qman_ccgrs), GFP_KERNEL); -+ if (!portal->ccgrs[ret]) -+ goto fail_ccgrs; -+ qman_ccgrs_init(&portal->ccgrs[ret][1]); -+ qman_ccgrs_fill(&portal->ccgrs[ret][0]); -+ INIT_LIST_HEAD(&portal->ccgr_cbs[ret]); -+ } -+ } -+ spin_lock_init(&portal->ccgr_lock); -+ portal->bits = 0; -+ portal->slowpoll = 0; -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ portal->eqci_owned = NULL; -+#endif -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ raw_spin_lock_init(&portal->sharing_lock); -+ portal->is_shared = config->public_cfg.is_shared; -+ portal->sharing_redirect = NULL; -+#endif -+ portal->sdqcr = QM_SDQCR_SOURCE_CHANNELS | QM_SDQCR_COUNT_UPTO3 | -+ QM_SDQCR_DEDICATED_PRECEDENCE | QM_SDQCR_TYPE_PRIO_QOS | -+ QM_SDQCR_TOKEN_SET(0xab) | QM_SDQCR_CHANNELS_DEDICATED; -+ portal->dqrr_disable_ref = 0; -+ portal->cb_dc_ern = NULL; -+ sprintf(buf, "qportal-%d", config->public_cfg.channel); -+ portal->pdev = platform_device_alloc(buf, -1); -+ if (!portal->pdev) { -+ pr_err("qman_portal - platform_device_alloc() failed\n"); -+ goto fail_devalloc; -+ } -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ portal->pdev->dev.coherent_dma_mask = DMA_BIT_MASK(40); -+ portal->pdev->dev.dma_mask = &portal->pdev->dev.coherent_dma_mask; -+#else -+ if (dma_set_mask(&portal->pdev->dev, DMA_BIT_MASK(40))) { -+ pr_err("qman_portal - dma_set_mask() failed\n"); -+ goto fail_devadd; -+ } -+#endif -+ portal->pdev->dev.pm_domain = &qman_portal_device_pm_domain; -+ portal->pdev->dev.platform_data = portal; -+ ret = platform_device_add(portal->pdev); -+ if (ret) { -+ pr_err("qman_portal - platform_device_add() failed\n"); -+ goto fail_devadd; -+ } -+ dpa_rbtree_init(&portal->retire_table); -+ isdr = 0xffffffff; -+ qm_isr_disable_write(__p, isdr); -+ portal->irq_sources = 0; -+ qm_isr_enable_write(__p, portal->irq_sources); -+ qm_isr_status_clear(__p, 0xffffffff); -+ snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, config->public_cfg.cpu); -+ if (request_irq(config->public_cfg.irq, portal_isr, 0, portal->irqname, -+ portal)) { -+ pr_err("request_irq() failed\n"); -+ goto fail_irq; -+ } -+ if ((config->public_cfg.cpu != -1) && -+ irq_can_set_affinity(config->public_cfg.irq) && -+ irq_set_affinity(config->public_cfg.irq, -+ cpumask_of(config->public_cfg.cpu))) { -+ pr_err("irq_set_affinity() failed\n"); -+ goto fail_affinity; -+ } -+ -+ /* Need EQCR to be empty before continuing */ -+ isdr ^= QM_PIRQ_EQCI; -+ qm_isr_disable_write(__p, isdr); -+ ret = qm_eqcr_get_fill(__p); -+ if (ret) { -+ pr_err("Qman EQCR unclean\n"); -+ goto fail_eqcr_empty; -+ } -+ isdr ^= (QM_PIRQ_DQRI | QM_PIRQ_MRI); -+ qm_isr_disable_write(__p, isdr); -+ if (qm_dqrr_current(__p) != NULL) { -+ pr_err("Qman DQRR unclean\n"); -+ qm_dqrr_cdc_consume_n(__p, 0xffff); -+ } -+ if (qm_mr_current(__p) != NULL) { -+ /* special handling, drain just in case it's a few FQRNIs */ -+ if (drain_mr_fqrni(__p)) { -+ const struct qm_mr_entry *e = qm_mr_current(__p); -+ /* -+ * Message ring cannot be empty no need to check -+ * qm_mr_current returned successfully -+ */ -+ pr_err("Qman MR unclean, MR VERB 0x%x, rc 0x%x\n, addr 0x%x", -+ e->verb, e->ern.rc, e->ern.fd.addr_lo); -+ goto fail_dqrr_mr_empty; -+ } -+ } -+ /* Success */ -+ portal->config = config; -+ qm_isr_disable_write(__p, 0); -+ qm_isr_uninhibit(__p); -+ /* Write a sane SDQCR */ -+ qm_dqrr_sdqcr_set(__p, portal->sdqcr); -+ return portal; -+fail_dqrr_mr_empty: -+fail_eqcr_empty: -+fail_affinity: -+ free_irq(config->public_cfg.irq, portal); -+fail_irq: -+ platform_device_del(portal->pdev); -+fail_devadd: -+ platform_device_put(portal->pdev); -+fail_devalloc: -+ if (num_ceetms) -+ for (ret = 0; ret < num_ceetms; ret++) -+ kfree(portal->ccgrs[ret]); -+fail_ccgrs: -+ kfree(portal->cgrs); -+fail_cgrs: -+ qm_isr_finish(__p); -+fail_isr: -+ qm_mc_finish(__p); -+fail_mc: -+ qm_mr_finish(__p); -+fail_mr: -+ qm_dqrr_finish(__p); -+fail_dqrr: -+ qm_eqcr_finish(__p); -+fail_eqcr: -+ if (portal->alloced) -+ kfree(portal); -+ return NULL; -+} -+ -+struct qman_portal *qman_create_affine_portal( -+ const struct qm_portal_config *config, -+ const struct qman_cgrs *cgrs) -+{ -+ struct qman_portal *res; -+ struct qman_portal *portal; -+ -+ portal = &per_cpu(qman_affine_portal, config->public_cfg.cpu); -+ res = qman_create_portal(portal, config, cgrs); -+ if (res) { -+ spin_lock(&affine_mask_lock); -+ cpumask_set_cpu(config->public_cfg.cpu, &affine_mask); -+ affine_channels[config->public_cfg.cpu] = -+ config->public_cfg.channel; -+ affine_portals[config->public_cfg.cpu] = portal; -+ spin_unlock(&affine_mask_lock); -+ } -+ return res; -+} -+ -+/* These checks are BUG_ON()s because the driver is already supposed to avoid -+ * these cases. */ -+struct qman_portal *qman_create_affine_slave(struct qman_portal *redirect, -+ int cpu) -+{ -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ struct qman_portal *p; -+ p = &per_cpu(qman_affine_portal, cpu); -+ /* Check that we don't already have our own portal */ -+ BUG_ON(p->config); -+ /* Check that we aren't already slaving to another portal */ -+ BUG_ON(p->is_shared); -+ /* Check that 'redirect' is prepared to have us */ -+ BUG_ON(!redirect->config->public_cfg.is_shared); -+ /* These are the only elements to initialise when redirecting */ -+ p->irq_sources = 0; -+ p->sharing_redirect = redirect; -+ affine_portals[cpu] = p; -+ return p; -+#else -+ BUG(); -+ return NULL; -+#endif -+} -+ -+void qman_destroy_portal(struct qman_portal *qm) -+{ -+ const struct qm_portal_config *pcfg; -+ int i; -+ -+ /* Stop dequeues on the portal */ -+ qm_dqrr_sdqcr_set(&qm->p, 0); -+ -+ /* NB we do this to "quiesce" EQCR. If we add enqueue-completions or -+ * something related to QM_PIRQ_EQCI, this may need fixing. -+ * Also, due to the prefetching model used for CI updates in the enqueue -+ * path, this update will only invalidate the CI cacheline *after* -+ * working on it, so we need to call this twice to ensure a full update -+ * irrespective of where the enqueue processing was at when the teardown -+ * began. */ -+ qm_eqcr_cce_update(&qm->p); -+ qm_eqcr_cce_update(&qm->p); -+ pcfg = qm->config; -+ -+ free_irq(pcfg->public_cfg.irq, qm); -+ -+ kfree(qm->cgrs); -+ if (num_ceetms) -+ for (i = 0; i < num_ceetms; i++) -+ kfree(qm->ccgrs[i]); -+ qm_isr_finish(&qm->p); -+ qm_mc_finish(&qm->p); -+ qm_mr_finish(&qm->p); -+ qm_dqrr_finish(&qm->p); -+ qm_eqcr_finish(&qm->p); -+ -+ platform_device_del(qm->pdev); -+ platform_device_put(qm->pdev); -+ -+ qm->config = NULL; -+ if (qm->alloced) -+ kfree(qm); -+} -+ -+const struct qm_portal_config *qman_destroy_affine_portal(void) -+{ -+ /* We don't want to redirect if we're a slave, use "raw" */ -+ struct qman_portal *qm = get_raw_affine_portal(); -+ const struct qm_portal_config *pcfg; -+ int cpu; -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (qm->sharing_redirect) { -+ qm->sharing_redirect = NULL; -+ put_affine_portal(); -+ return NULL; -+ } -+ qm->is_shared = 0; -+#endif -+ pcfg = qm->config; -+ cpu = pcfg->public_cfg.cpu; -+ -+ qman_destroy_portal(qm); -+ -+ spin_lock(&affine_mask_lock); -+ cpumask_clear_cpu(cpu, &affine_mask); -+ spin_unlock(&affine_mask_lock); -+ put_affine_portal(); -+ return pcfg; -+} -+ -+const struct qman_portal_config *qman_p_get_portal_config(struct qman_portal *p) -+{ -+ return &p->config->public_cfg; -+} -+EXPORT_SYMBOL(qman_p_get_portal_config); -+ -+const struct qman_portal_config *qman_get_portal_config(void) -+{ -+ struct qman_portal *p = get_affine_portal(); -+ const struct qman_portal_config *ret = qman_p_get_portal_config(p); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_get_portal_config); -+ -+/* Inline helper to reduce nesting in __poll_portal_slow() */ -+static inline void fq_state_change(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_mr_entry *msg, u8 verb) -+{ -+ FQLOCK(fq); -+ switch (verb) { -+ case QM_MR_VERB_FQRL: -+ DPA_ASSERT(fq_isset(fq, QMAN_FQ_STATE_ORL)); -+ fq_clear(fq, QMAN_FQ_STATE_ORL); -+ table_del_fq(p, fq); -+ break; -+ case QM_MR_VERB_FQRN: -+ DPA_ASSERT((fq->state == qman_fq_state_parked) || -+ (fq->state == qman_fq_state_sched)); -+ DPA_ASSERT(fq_isset(fq, QMAN_FQ_STATE_CHANGING)); -+ fq_clear(fq, QMAN_FQ_STATE_CHANGING); -+ if (msg->fq.fqs & QM_MR_FQS_NOTEMPTY) -+ fq_set(fq, QMAN_FQ_STATE_NE); -+ if (msg->fq.fqs & QM_MR_FQS_ORLPRESENT) -+ fq_set(fq, QMAN_FQ_STATE_ORL); -+ else -+ table_del_fq(p, fq); -+ fq->state = qman_fq_state_retired; -+ break; -+ case QM_MR_VERB_FQPN: -+ DPA_ASSERT(fq->state == qman_fq_state_sched); -+ DPA_ASSERT(fq_isclear(fq, QMAN_FQ_STATE_CHANGING)); -+ fq->state = qman_fq_state_parked; -+ } -+ FQUNLOCK(fq); -+} -+ -+static u32 __poll_portal_slow(struct qman_portal *p, u32 is) -+{ -+ const struct qm_mr_entry *msg; -+ struct qm_mr_entry swapped_msg; -+ int k; -+ -+ if (is & QM_PIRQ_CSCI) { -+ struct qman_cgrs rr, c; -+ struct qm_mc_result *mcr; -+ struct qman_cgr *cgr; -+ unsigned long irqflags __maybe_unused; -+ -+ spin_lock_irqsave(&p->cgr_lock, irqflags); -+ /* -+ * The CSCI bit must be cleared _before_ issuing the -+ * Query Congestion State command, to ensure that a long -+ * CGR State Change callback cannot miss an intervening -+ * state change. -+ */ -+ qm_isr_status_clear(&p->p, QM_PIRQ_CSCI); -+ qm_mc_start(&p->p); -+ qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCONGESTION); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ for (k = 0; k < 8; k++) -+ mcr->querycongestion.state.__state[k] = be32_to_cpu( -+ mcr->querycongestion.state.__state[k]); -+ /* mask out the ones I'm not interested in */ -+ qman_cgrs_and(&rr, (const struct qman_cgrs *) -+ &mcr->querycongestion.state, &p->cgrs[0]); -+ /* check previous snapshot for delta, enter/exit congestion */ -+ qman_cgrs_xor(&c, &rr, &p->cgrs[1]); -+ /* update snapshot */ -+ qman_cgrs_cp(&p->cgrs[1], &rr); -+ /* Invoke callback */ -+ list_for_each_entry(cgr, &p->cgr_cbs, node) -+ if (cgr->cb && qman_cgrs_get(&c, cgr->cgrid)) -+ cgr->cb(p, cgr, qman_cgrs_get(&rr, cgr->cgrid)); -+ spin_unlock_irqrestore(&p->cgr_lock, irqflags); -+ } -+ if (is & QM_PIRQ_CCSCI) { -+ struct qman_ccgrs rr, c, congestion_result; -+ struct qm_mc_result *mcr; -+ struct qm_mc_command *mcc; -+ struct qm_ceetm_ccg *ccg; -+ unsigned long irqflags __maybe_unused; -+ int i, j; -+ -+ spin_lock_irqsave(&p->ccgr_lock, irqflags); -+ /* -+ * The CCSCI bit must be cleared _before_ issuing the -+ * Query Congestion State command, to ensure that a long -+ * CCGR State Change callback cannot miss an intervening -+ * state change. -+ */ -+ qm_isr_status_clear(&p->p, QM_PIRQ_CCSCI); -+ -+ for (i = 0; i < num_ceetms; i++) { -+ for (j = 0; j < 2; j++) { -+ mcc = qm_mc_start(&p->p); -+ mcc->ccgr_query.ccgrid = cpu_to_be16( -+ CEETM_QUERY_CONGESTION_STATE | j); -+ mcc->ccgr_query.dcpid = i; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CCGR_QUERY); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ for (k = 0; k < 8; k++) -+ mcr->ccgr_query.congestion_state.state. -+ __state[k] = be32_to_cpu( -+ mcr->ccgr_query. -+ congestion_state.state. -+ __state[k]); -+ congestion_result.q[j] = -+ mcr->ccgr_query.congestion_state.state; -+ } -+ /* mask out the ones I'm not interested in */ -+ qman_ccgrs_and(&rr, &congestion_result, -+ &p->ccgrs[i][0]); -+ /* -+ * check previous snapshot for delta, enter/exit -+ * congestion. -+ */ -+ qman_ccgrs_xor(&c, &rr, &p->ccgrs[i][1]); -+ /* update snapshot */ -+ qman_ccgrs_cp(&p->ccgrs[i][1], &rr); -+ /* Invoke callback */ -+ list_for_each_entry(ccg, &p->ccgr_cbs[i], cb_node) -+ if (ccg->cb && qman_ccgrs_get(&c, -+ (ccg->parent->idx << 4) | ccg->idx)) -+ ccg->cb(ccg, ccg->cb_ctx, -+ qman_ccgrs_get(&rr, -+ (ccg->parent->idx << 4) -+ | ccg->idx)); -+ } -+ spin_unlock_irqrestore(&p->ccgr_lock, irqflags); -+ } -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (is & QM_PIRQ_EQCI) { -+ unsigned long irqflags; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ p->eqci_owned = NULL; -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ wake_up(&affine_queue); -+ } -+#endif -+ -+ if (is & QM_PIRQ_EQRI) { -+ unsigned long irqflags __maybe_unused; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ qm_eqcr_cce_update(&p->p); -+ qm_eqcr_set_ithresh(&p->p, 0); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ wake_up(&affine_queue); -+ } -+ -+ if (is & QM_PIRQ_MRI) { -+ struct qman_fq *fq; -+ u8 verb, num = 0; -+mr_loop: -+ qm_mr_pvb_update(&p->p); -+ msg = qm_mr_current(&p->p); -+ if (!msg) -+ goto mr_done; -+ swapped_msg = *msg; -+ hw_fd_to_cpu(&swapped_msg.ern.fd); -+ verb = msg->verb & QM_MR_VERB_TYPE_MASK; -+ /* The message is a software ERN iff the 0x20 bit is set */ -+ if (verb & 0x20) { -+ switch (verb) { -+ case QM_MR_VERB_FQRNI: -+ /* nada, we drop FQRNIs on the floor */ -+ break; -+ case QM_MR_VERB_FQRN: -+ case QM_MR_VERB_FQRL: -+ /* Lookup in the retirement table */ -+ fq = table_find_fq(p, be32_to_cpu(msg->fq.fqid)); -+ BUG_ON(!fq); -+ fq_state_change(p, fq, &swapped_msg, verb); -+ if (fq->cb.fqs) -+ fq->cb.fqs(p, fq, &swapped_msg); -+ break; -+ case QM_MR_VERB_FQPN: -+ /* Parked */ -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ fq = get_fq_table_entry( -+ be32_to_cpu(msg->fq.contextB)); -+#else -+ fq = (void *)(uintptr_t) -+ be32_to_cpu(msg->fq.contextB); -+#endif -+ fq_state_change(p, fq, msg, verb); -+ if (fq->cb.fqs) -+ fq->cb.fqs(p, fq, &swapped_msg); -+ break; -+ case QM_MR_VERB_DC_ERN: -+ /* DCP ERN */ -+ if (p->cb_dc_ern) -+ p->cb_dc_ern(p, msg); -+ else if (cb_dc_ern) -+ cb_dc_ern(p, msg); -+ else { -+ static int warn_once; -+ if (!warn_once) { -+ pr_crit("Leaking DCP ERNs!\n"); -+ warn_once = 1; -+ } -+ } -+ break; -+ default: -+ pr_crit("Invalid MR verb 0x%02x\n", verb); -+ } -+ } else { -+ /* Its a software ERN */ -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ fq = get_fq_table_entry(be32_to_cpu(msg->ern.tag)); -+#else -+ fq = (void *)(uintptr_t)be32_to_cpu(msg->ern.tag); -+#endif -+ fq->cb.ern(p, fq, &swapped_msg); -+ } -+ num++; -+ qm_mr_next(&p->p); -+ goto mr_loop; -+mr_done: -+ qm_mr_cci_consume(&p->p, num); -+ } -+ /* -+ * QM_PIRQ_CSCI/CCSCI has already been cleared, as part of its specific -+ * processing. If that interrupt source has meanwhile been re-asserted, -+ * we mustn't clear it here (or in the top-level interrupt handler). -+ */ -+ return is & (QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI); -+} -+ -+/* remove some slowish-path stuff from the "fast path" and make sure it isn't -+ * inlined. */ -+static noinline void clear_vdqcr(struct qman_portal *p, struct qman_fq *fq) -+{ -+ p->vdqcr_owned = NULL; -+ FQLOCK(fq); -+ fq_clear(fq, QMAN_FQ_STATE_VDQCR); -+ FQUNLOCK(fq); -+ wake_up(&affine_queue); -+} -+ -+/* Copy a DQRR entry ensuring reads reach QBMan in order */ -+static inline void safe_copy_dqrr(struct qm_dqrr_entry *dst, -+ const struct qm_dqrr_entry *src) -+{ -+ int i = 0; -+ const u64 *s64 = (u64*)src; -+ u64 *d64 = (u64*)dst; -+ -+ /* DQRR only has 32 bytes of valid data so only need to -+ * copy 4 - 64 bit values */ -+ *d64 = *s64; -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ { -+ u32 res, zero = 0; -+ /* Create a dependancy after copying first bytes ensures no wrap -+ transaction generated to QBMan */ -+ /* Logical AND the value pointed to by s64 with 0x0 and -+ store the result in res */ -+ asm volatile("and %[result], %[in1], %[in2]" -+ : [result] "=r" (res) -+ : [in1] "r" (zero), [in2] "r" (*s64) -+ : "memory"); -+ /* Add res to s64 - this creates a dependancy on the result of -+ reading the value of s64 before the next read. The side -+ effect of this is that the core must stall until the first -+ aligned read is complete therefore preventing a WRAP -+ transaction to be seen by the QBMan */ -+ asm volatile("add %[result], %[in1], %[in2]" -+ : [result] "=r" (s64) -+ : [in1] "r" (res), [in2] "r" (s64) -+ : "memory"); -+ } -+#endif -+ /* Copy the last 3 64 bit parts */ -+ d64++; s64++; -+ for (;i<3; i++) -+ *d64++ = *s64++; -+} -+ -+/* Look: no locks, no irq_save()s, no preempt_disable()s! :-) The only states -+ * that would conflict with other things if they ran at the same time on the -+ * same cpu are; -+ * -+ * (i) setting/clearing vdqcr_owned, and -+ * (ii) clearing the NE (Not Empty) flag. -+ * -+ * Both are safe. Because; -+ * -+ * (i) this clearing can only occur after qman_volatile_dequeue() has set the -+ * vdqcr_owned field (which it does before setting VDQCR), and -+ * qman_volatile_dequeue() blocks interrupts and preemption while this is -+ * done so that we can't interfere. -+ * (ii) the NE flag is only cleared after qman_retire_fq() has set it, and as -+ * with (i) that API prevents us from interfering until it's safe. -+ * -+ * The good thing is that qman_volatile_dequeue() and qman_retire_fq() run far -+ * less frequently (ie. per-FQ) than __poll_portal_fast() does, so the nett -+ * advantage comes from this function not having to "lock" anything at all. -+ * -+ * Note also that the callbacks are invoked at points which are safe against the -+ * above potential conflicts, but that this function itself is not re-entrant -+ * (this is because the function tracks one end of each FIFO in the portal and -+ * we do *not* want to lock that). So the consequence is that it is safe for -+ * user callbacks to call into any Qman API *except* qman_poll() (as that's the -+ * sole API that could be invoking the callback through this function). -+ */ -+static inline unsigned int __poll_portal_fast(struct qman_portal *p, -+ unsigned int poll_limit) -+{ -+ const struct qm_dqrr_entry *dq; -+ struct qman_fq *fq; -+ enum qman_cb_dqrr_result res; -+ unsigned int limit = 0; -+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -+ struct qm_dqrr_entry *shadow; -+ const struct qm_dqrr_entry *orig_dq; -+#endif -+loop: -+ qm_dqrr_pvb_update(&p->p); -+ dq = qm_dqrr_current(&p->p); -+ if (!dq) -+ goto done; -+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -+ /* If running on an LE system the fields of the -+ dequeue entry must be swapped. Because the -+ QMan HW will ignore writes the DQRR entry is -+ copied and the index stored within the copy */ -+ shadow = &p->shadow_dqrr[DQRR_PTR2IDX(dq)]; -+ /* Use safe copy here to avoid WRAP transaction */ -+ safe_copy_dqrr(shadow, dq); -+ orig_dq = dq; -+ dq = shadow; -+ shadow->fqid = be32_to_cpu(shadow->fqid); -+ shadow->contextB = be32_to_cpu(shadow->contextB); -+ shadow->seqnum = be16_to_cpu(shadow->seqnum); -+ hw_fd_to_cpu(&shadow->fd); -+#endif -+ if (dq->stat & QM_DQRR_STAT_UNSCHEDULED) { -+ /* VDQCR: don't trust contextB as the FQ may have been -+ * configured for h/w consumption and we're draining it -+ * post-retirement. */ -+ fq = p->vdqcr_owned; -+ /* We only set QMAN_FQ_STATE_NE when retiring, so we only need -+ * to check for clearing it when doing volatile dequeues. It's -+ * one less thing to check in the critical path (SDQCR). */ -+ if (dq->stat & QM_DQRR_STAT_FQ_EMPTY) -+ fq_clear(fq, QMAN_FQ_STATE_NE); -+ /* this is duplicated from the SDQCR code, but we have stuff to -+ * do before *and* after this callback, and we don't want -+ * multiple if()s in the critical path (SDQCR). */ -+ res = fq->cb.dqrr(p, fq, dq); -+ if (res == qman_cb_dqrr_stop) -+ goto done; -+ /* Check for VDQCR completion */ -+ if (dq->stat & QM_DQRR_STAT_DQCR_EXPIRED) -+ clear_vdqcr(p, fq); -+ } else { -+ /* SDQCR: contextB points to the FQ */ -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ fq = get_fq_table_entry(dq->contextB); -+#else -+ fq = (void *)(uintptr_t)dq->contextB; -+#endif -+ /* Now let the callback do its stuff */ -+ res = fq->cb.dqrr(p, fq, dq); -+ -+ /* The callback can request that we exit without consuming this -+ * entry nor advancing; */ -+ if (res == qman_cb_dqrr_stop) -+ goto done; -+ } -+ /* Interpret 'dq' from a driver perspective. */ -+ /* Parking isn't possible unless HELDACTIVE was set. NB, -+ * FORCEELIGIBLE implies HELDACTIVE, so we only need to -+ * check for HELDACTIVE to cover both. */ -+ DPA_ASSERT((dq->stat & QM_DQRR_STAT_FQ_HELDACTIVE) || -+ (res != qman_cb_dqrr_park)); -+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -+ if (res != qman_cb_dqrr_defer) -+ qm_dqrr_cdc_consume_1ptr(&p->p, orig_dq, -+ (res == qman_cb_dqrr_park)); -+#else -+ /* Defer just means "skip it, I'll consume it myself later on" */ -+ if (res != qman_cb_dqrr_defer) -+ qm_dqrr_cdc_consume_1ptr(&p->p, dq, (res == qman_cb_dqrr_park)); -+#endif -+ /* Move forward */ -+ qm_dqrr_next(&p->p); -+ /* Entry processed and consumed, increment our counter. The callback can -+ * request that we exit after consuming the entry, and we also exit if -+ * we reach our processing limit, so loop back only if neither of these -+ * conditions is met. */ -+ if ((++limit < poll_limit) && (res != qman_cb_dqrr_consume_stop)) -+ goto loop; -+done: -+ return limit; -+} -+ -+u32 qman_irqsource_get(void) -+{ -+ /* "irqsource" and "poll" APIs mustn't redirect when sharing, they -+ * should shut the user out if they are not the primary CPU hosting the -+ * portal. That's why we use the "raw" interface. */ -+ struct qman_portal *p = get_raw_affine_portal(); -+ u32 ret = p->irq_sources & QM_PIRQ_VISIBLE; -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_irqsource_get); -+ -+int qman_p_irqsource_add(struct qman_portal *p, u32 bits __maybe_unused) -+{ -+ __maybe_unused unsigned long irqflags; -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (p->sharing_redirect) -+ return -EINVAL; -+ else -+#endif -+ { -+ bits = bits & QM_PIRQ_VISIBLE; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ /* Clear any previously remaining interrupt conditions in -+ * QCSP_ISR. This prevents raising a false interrupt when -+ * interrupt conditions are enabled in QCSP_IER. -+ */ -+ qm_isr_status_clear(&p->p, bits); -+ set_bits(bits, &p->irq_sources); -+ qm_isr_enable_write(&p->p, p->irq_sources); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ } -+ return 0; -+} -+EXPORT_SYMBOL(qman_p_irqsource_add); -+ -+int qman_irqsource_add(u32 bits __maybe_unused) -+{ -+ struct qman_portal *p = get_raw_affine_portal(); -+ int ret; -+ ret = qman_p_irqsource_add(p, bits); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_irqsource_add); -+ -+int qman_p_irqsource_remove(struct qman_portal *p, u32 bits) -+{ -+ __maybe_unused unsigned long irqflags; -+ u32 ier; -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (p->sharing_redirect) { -+ put_affine_portal(); -+ return -EINVAL; -+ } -+#endif -+ /* Our interrupt handler only processes+clears status register bits that -+ * are in p->irq_sources. As we're trimming that mask, if one of them -+ * were to assert in the status register just before we remove it from -+ * the enable register, there would be an interrupt-storm when we -+ * release the IRQ lock. So we wait for the enable register update to -+ * take effect in h/w (by reading it back) and then clear all other bits -+ * in the status register. Ie. we clear them from ISR once it's certain -+ * IER won't allow them to reassert. */ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ bits &= QM_PIRQ_VISIBLE; -+ clear_bits(bits, &p->irq_sources); -+ qm_isr_enable_write(&p->p, p->irq_sources); -+ -+ ier = qm_isr_enable_read(&p->p); -+ /* Using "~ier" (rather than "bits" or "~p->irq_sources") creates a -+ * data-dependency, ie. to protect against re-ordering. */ -+ qm_isr_status_clear(&p->p, ~ier); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ return 0; -+} -+EXPORT_SYMBOL(qman_p_irqsource_remove); -+ -+int qman_irqsource_remove(u32 bits) -+{ -+ struct qman_portal *p = get_raw_affine_portal(); -+ int ret; -+ ret = qman_p_irqsource_remove(p, bits); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_irqsource_remove); -+ -+const cpumask_t *qman_affine_cpus(void) -+{ -+ return &affine_mask; -+} -+EXPORT_SYMBOL(qman_affine_cpus); -+ -+u16 qman_affine_channel(int cpu) -+{ -+ if (cpu < 0) { -+ struct qman_portal *portal = get_raw_affine_portal(); -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ BUG_ON(portal->sharing_redirect); -+#endif -+ cpu = portal->config->public_cfg.cpu; -+ put_affine_portal(); -+ } -+ BUG_ON(!cpumask_test_cpu(cpu, &affine_mask)); -+ return affine_channels[cpu]; -+} -+EXPORT_SYMBOL(qman_affine_channel); -+ -+void *qman_get_affine_portal(int cpu) -+{ -+ return affine_portals[cpu]; -+} -+EXPORT_SYMBOL(qman_get_affine_portal); -+ -+int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit) -+{ -+ int ret; -+ -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (unlikely(p->sharing_redirect)) -+ ret = -EINVAL; -+ else -+#endif -+ { -+ BUG_ON(p->irq_sources & QM_PIRQ_DQRI); -+ ret = __poll_portal_fast(p, limit); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(qman_p_poll_dqrr); -+ -+int qman_poll_dqrr(unsigned int limit) -+{ -+ struct qman_portal *p = get_poll_portal(); -+ int ret; -+ ret = qman_p_poll_dqrr(p, limit); -+ put_poll_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_poll_dqrr); -+ -+u32 qman_p_poll_slow(struct qman_portal *p) -+{ -+ u32 ret; -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (unlikely(p->sharing_redirect)) -+ ret = (u32)-1; -+ else -+#endif -+ { -+ u32 is = qm_isr_status_read(&p->p) & ~p->irq_sources; -+ ret = __poll_portal_slow(p, is); -+ qm_isr_status_clear(&p->p, ret); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(qman_p_poll_slow); -+ -+u32 qman_poll_slow(void) -+{ -+ struct qman_portal *p = get_poll_portal(); -+ u32 ret; -+ ret = qman_p_poll_slow(p); -+ put_poll_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_poll_slow); -+ -+/* Legacy wrapper */ -+void qman_p_poll(struct qman_portal *p) -+{ -+#ifdef CONFIG_FSL_DPA_PORTAL_SHARE -+ if (unlikely(p->sharing_redirect)) -+ return; -+#endif -+ if ((~p->irq_sources) & QM_PIRQ_SLOW) { -+ if (!(p->slowpoll--)) { -+ u32 is = qm_isr_status_read(&p->p) & ~p->irq_sources; -+ u32 active = __poll_portal_slow(p, is); -+ if (active) { -+ qm_isr_status_clear(&p->p, active); -+ p->slowpoll = SLOW_POLL_BUSY; -+ } else -+ p->slowpoll = SLOW_POLL_IDLE; -+ } -+ } -+ if ((~p->irq_sources) & QM_PIRQ_DQRI) -+ __poll_portal_fast(p, CONFIG_FSL_QMAN_POLL_LIMIT); -+} -+EXPORT_SYMBOL(qman_p_poll); -+ -+void qman_poll(void) -+{ -+ struct qman_portal *p = get_poll_portal(); -+ qman_p_poll(p); -+ put_poll_portal(); -+} -+EXPORT_SYMBOL(qman_poll); -+ -+void qman_p_stop_dequeues(struct qman_portal *p) -+{ -+ qman_stop_dequeues_ex(p); -+} -+EXPORT_SYMBOL(qman_p_stop_dequeues); -+ -+void qman_stop_dequeues(void) -+{ -+ struct qman_portal *p = get_affine_portal(); -+ qman_p_stop_dequeues(p); -+ put_affine_portal(); -+} -+EXPORT_SYMBOL(qman_stop_dequeues); -+ -+void qman_p_start_dequeues(struct qman_portal *p) -+{ -+ unsigned long irqflags __maybe_unused; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ DPA_ASSERT(p->dqrr_disable_ref > 0); -+ if (!(--p->dqrr_disable_ref)) -+ qm_dqrr_set_maxfill(&p->p, DQRR_MAXFILL); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+} -+EXPORT_SYMBOL(qman_p_start_dequeues); -+ -+void qman_start_dequeues(void) -+{ -+ struct qman_portal *p = get_affine_portal(); -+ qman_p_start_dequeues(p); -+ put_affine_portal(); -+} -+EXPORT_SYMBOL(qman_start_dequeues); -+ -+void qman_p_static_dequeue_add(struct qman_portal *p, u32 pools) -+{ -+ unsigned long irqflags __maybe_unused; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ pools &= p->config->public_cfg.pools; -+ p->sdqcr |= pools; -+ qm_dqrr_sdqcr_set(&p->p, p->sdqcr); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+} -+EXPORT_SYMBOL(qman_p_static_dequeue_add); -+ -+void qman_static_dequeue_add(u32 pools) -+{ -+ struct qman_portal *p = get_affine_portal(); -+ qman_p_static_dequeue_add(p, pools); -+ put_affine_portal(); -+} -+EXPORT_SYMBOL(qman_static_dequeue_add); -+ -+void qman_p_static_dequeue_del(struct qman_portal *p, u32 pools) -+{ -+ unsigned long irqflags __maybe_unused; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ pools &= p->config->public_cfg.pools; -+ p->sdqcr &= ~pools; -+ qm_dqrr_sdqcr_set(&p->p, p->sdqcr); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+} -+EXPORT_SYMBOL(qman_p_static_dequeue_del); -+ -+void qman_static_dequeue_del(u32 pools) -+{ -+ struct qman_portal *p = get_affine_portal(); -+ qman_p_static_dequeue_del(p, pools); -+ put_affine_portal(); -+} -+EXPORT_SYMBOL(qman_static_dequeue_del); -+ -+u32 qman_p_static_dequeue_get(struct qman_portal *p) -+{ -+ return p->sdqcr; -+} -+EXPORT_SYMBOL(qman_p_static_dequeue_get); -+ -+u32 qman_static_dequeue_get(void) -+{ -+ struct qman_portal *p = get_affine_portal(); -+ u32 ret = qman_p_static_dequeue_get(p); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_static_dequeue_get); -+ -+void qman_p_dca(struct qman_portal *p, struct qm_dqrr_entry *dq, -+ int park_request) -+{ -+ qm_dqrr_cdc_consume_1ptr(&p->p, dq, park_request); -+} -+EXPORT_SYMBOL(qman_p_dca); -+ -+void qman_dca(struct qm_dqrr_entry *dq, int park_request) -+{ -+ struct qman_portal *p = get_affine_portal(); -+ qman_p_dca(p, dq, park_request); -+ put_affine_portal(); -+} -+EXPORT_SYMBOL(qman_dca); -+ -+/*******************/ -+/* Frame queue API */ -+/*******************/ -+ -+static const char *mcr_result_str(u8 result) -+{ -+ switch (result) { -+ case QM_MCR_RESULT_NULL: -+ return "QM_MCR_RESULT_NULL"; -+ case QM_MCR_RESULT_OK: -+ return "QM_MCR_RESULT_OK"; -+ case QM_MCR_RESULT_ERR_FQID: -+ return "QM_MCR_RESULT_ERR_FQID"; -+ case QM_MCR_RESULT_ERR_FQSTATE: -+ return "QM_MCR_RESULT_ERR_FQSTATE"; -+ case QM_MCR_RESULT_ERR_NOTEMPTY: -+ return "QM_MCR_RESULT_ERR_NOTEMPTY"; -+ case QM_MCR_RESULT_PENDING: -+ return "QM_MCR_RESULT_PENDING"; -+ case QM_MCR_RESULT_ERR_BADCOMMAND: -+ return "QM_MCR_RESULT_ERR_BADCOMMAND"; -+ } -+ return "<unknown MCR result>"; -+} -+ -+int qman_create_fq(u32 fqid, u32 flags, struct qman_fq *fq) -+{ -+ struct qm_fqd fqd; -+ struct qm_mcr_queryfq_np np; -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ -+ if (flags & QMAN_FQ_FLAG_DYNAMIC_FQID) { -+ int ret = qman_alloc_fqid(&fqid); -+ if (ret) -+ return ret; -+ } -+ spin_lock_init(&fq->fqlock); -+ fq->fqid = fqid; -+ fq->flags = flags; -+ fq->state = qman_fq_state_oos; -+ fq->cgr_groupid = 0; -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ if (unlikely(find_empty_fq_table_entry(&fq->key, fq))) -+ return -ENOMEM; -+#endif -+ if (!(flags & QMAN_FQ_FLAG_AS_IS) || (flags & QMAN_FQ_FLAG_NO_MODIFY)) -+ return 0; -+ /* Everything else is AS_IS support */ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ mcc = qm_mc_start(&p->p); -+ mcc->queryfq.fqid = cpu_to_be32(fqid); -+ qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_QUERYFQ); -+ if (mcr->result != QM_MCR_RESULT_OK) { -+ pr_err("QUERYFQ failed: %s\n", mcr_result_str(mcr->result)); -+ goto err; -+ } -+ fqd = mcr->queryfq.fqd; -+ hw_fqd_to_cpu(&fqd); -+ mcc = qm_mc_start(&p->p); -+ mcc->queryfq_np.fqid = cpu_to_be32(fqid); -+ qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_QUERYFQ_NP); -+ if (mcr->result != QM_MCR_RESULT_OK) { -+ pr_err("QUERYFQ_NP failed: %s\n", mcr_result_str(mcr->result)); -+ goto err; -+ } -+ np = mcr->queryfq_np; -+ /* Phew, have queryfq and queryfq_np results, stitch together -+ * the FQ object from those. */ -+ fq->cgr_groupid = fqd.cgid; -+ switch (np.state & QM_MCR_NP_STATE_MASK) { -+ case QM_MCR_NP_STATE_OOS: -+ break; -+ case QM_MCR_NP_STATE_RETIRED: -+ fq->state = qman_fq_state_retired; -+ if (np.frm_cnt) -+ fq_set(fq, QMAN_FQ_STATE_NE); -+ break; -+ case QM_MCR_NP_STATE_TEN_SCHED: -+ case QM_MCR_NP_STATE_TRU_SCHED: -+ case QM_MCR_NP_STATE_ACTIVE: -+ fq->state = qman_fq_state_sched; -+ if (np.state & QM_MCR_NP_STATE_R) -+ fq_set(fq, QMAN_FQ_STATE_CHANGING); -+ break; -+ case QM_MCR_NP_STATE_PARKED: -+ fq->state = qman_fq_state_parked; -+ break; -+ default: -+ DPA_ASSERT(NULL == "invalid FQ state"); -+ } -+ if (fqd.fq_ctrl & QM_FQCTRL_CGE) -+ fq->state |= QMAN_FQ_STATE_CGR_EN; -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return 0; -+err: -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (flags & QMAN_FQ_FLAG_DYNAMIC_FQID) -+ qman_release_fqid(fqid); -+ return -EIO; -+} -+EXPORT_SYMBOL(qman_create_fq); -+ -+void qman_destroy_fq(struct qman_fq *fq, u32 flags __maybe_unused) -+{ -+ -+ /* We don't need to lock the FQ as it is a pre-condition that the FQ be -+ * quiesced. Instead, run some checks. */ -+ switch (fq->state) { -+ case qman_fq_state_parked: -+ DPA_ASSERT(flags & QMAN_FQ_DESTROY_PARKED); -+ case qman_fq_state_oos: -+ if (fq_isset(fq, QMAN_FQ_FLAG_DYNAMIC_FQID)) -+ qman_release_fqid(fq->fqid); -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ clear_fq_table_entry(fq->key); -+#endif -+ return; -+ default: -+ break; -+ } -+ DPA_ASSERT(NULL == "qman_free_fq() on unquiesced FQ!"); -+} -+EXPORT_SYMBOL(qman_destroy_fq); -+ -+u32 qman_fq_fqid(struct qman_fq *fq) -+{ -+ return fq->fqid; -+} -+EXPORT_SYMBOL(qman_fq_fqid); -+ -+void qman_fq_state(struct qman_fq *fq, enum qman_fq_state *state, u32 *flags) -+{ -+ if (state) -+ *state = fq->state; -+ if (flags) -+ *flags = fq->flags; -+} -+EXPORT_SYMBOL(qman_fq_state); -+ -+int qman_init_fq(struct qman_fq *fq, u32 flags, struct qm_mcc_initfq *opts) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res, myverb = (flags & QMAN_INITFQ_FLAG_SCHED) ? -+ QM_MCC_VERB_INITFQ_SCHED : QM_MCC_VERB_INITFQ_PARKED; -+ -+ if ((fq->state != qman_fq_state_oos) && -+ (fq->state != qman_fq_state_parked)) -+ return -EINVAL; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))) -+ return -EINVAL; -+#endif -+ if (opts && (opts->we_mask & QM_INITFQ_WE_OAC)) { -+ /* And can't be set at the same time as TDTHRESH */ -+ if (opts->we_mask & QM_INITFQ_WE_TDTHRESH) -+ return -EINVAL; -+ } -+ /* Issue an INITFQ_[PARKED|SCHED] management command */ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ FQLOCK(fq); -+ if (unlikely((fq_isset(fq, QMAN_FQ_STATE_CHANGING)) || -+ ((fq->state != qman_fq_state_oos) && -+ (fq->state != qman_fq_state_parked)))) { -+ FQUNLOCK(fq); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return -EBUSY; -+ } -+ mcc = qm_mc_start(&p->p); -+ if (opts) -+ mcc->initfq = *opts; -+ mcc->initfq.fqid = cpu_to_be32(fq->fqid); -+ mcc->initfq.count = 0; -+ -+ /* If the FQ does *not* have the TO_DCPORTAL flag, contextB is set as a -+ * demux pointer. Otherwise, the caller-provided value is allowed to -+ * stand, don't overwrite it. */ -+ if (fq_isclear(fq, QMAN_FQ_FLAG_TO_DCPORTAL)) { -+ dma_addr_t phys_fq; -+ mcc->initfq.we_mask |= QM_INITFQ_WE_CONTEXTB; -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ mcc->initfq.fqd.context_b = fq->key; -+#else -+ mcc->initfq.fqd.context_b = (u32)(uintptr_t)fq; -+#endif -+ /* and the physical address - NB, if the user wasn't trying to -+ * set CONTEXTA, clear the stashing settings. */ -+ if (!(mcc->initfq.we_mask & QM_INITFQ_WE_CONTEXTA)) { -+ mcc->initfq.we_mask |= QM_INITFQ_WE_CONTEXTA; -+ memset(&mcc->initfq.fqd.context_a, 0, -+ sizeof(mcc->initfq.fqd.context_a)); -+ } else { -+ phys_fq = dma_map_single(&p->pdev->dev, fq, sizeof(*fq), -+ DMA_TO_DEVICE); -+ qm_fqd_stashing_set64(&mcc->initfq.fqd, phys_fq); -+ } -+ } -+ if (flags & QMAN_INITFQ_FLAG_LOCAL) { -+ mcc->initfq.fqd.dest.channel = p->config->public_cfg.channel; -+ if (!(mcc->initfq.we_mask & QM_INITFQ_WE_DESTWQ)) { -+ mcc->initfq.we_mask |= QM_INITFQ_WE_DESTWQ; -+ mcc->initfq.fqd.dest.wq = 4; -+ } -+ } -+ mcc->initfq.we_mask = cpu_to_be16(mcc->initfq.we_mask); -+ cpu_to_hw_fqd(&mcc->initfq.fqd); -+ qm_mc_commit(&p->p, myverb); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == myverb); -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ FQUNLOCK(fq); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return -EIO; -+ } -+ if (opts) { -+ if (opts->we_mask & QM_INITFQ_WE_FQCTRL) { -+ if (opts->fqd.fq_ctrl & QM_FQCTRL_CGE) -+ fq_set(fq, QMAN_FQ_STATE_CGR_EN); -+ else -+ fq_clear(fq, QMAN_FQ_STATE_CGR_EN); -+ } -+ if (opts->we_mask & QM_INITFQ_WE_CGID) -+ fq->cgr_groupid = opts->fqd.cgid; -+ } -+ fq->state = (flags & QMAN_INITFQ_FLAG_SCHED) ? -+ qman_fq_state_sched : qman_fq_state_parked; -+ FQUNLOCK(fq); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return 0; -+} -+EXPORT_SYMBOL(qman_init_fq); -+ -+int qman_schedule_fq(struct qman_fq *fq) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ int ret = 0; -+ u8 res; -+ -+ if (fq->state != qman_fq_state_parked) -+ return -EINVAL; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))) -+ return -EINVAL; -+#endif -+ /* Issue a ALTERFQ_SCHED management command */ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ FQLOCK(fq); -+ if (unlikely((fq_isset(fq, QMAN_FQ_STATE_CHANGING)) || -+ (fq->state != qman_fq_state_parked))) { -+ ret = -EBUSY; -+ goto out; -+ } -+ mcc = qm_mc_start(&p->p); -+ mcc->alterfq.fqid = cpu_to_be32(fq->fqid); -+ qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_SCHED); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_SCHED); -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ ret = -EIO; -+ goto out; -+ } -+ fq->state = qman_fq_state_sched; -+out: -+ FQUNLOCK(fq); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_schedule_fq); -+ -+int qman_retire_fq(struct qman_fq *fq, u32 *flags) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ int rval; -+ u8 res; -+ -+ if ((fq->state != qman_fq_state_parked) && -+ (fq->state != qman_fq_state_sched)) -+ return -EINVAL; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))) -+ return -EINVAL; -+#endif -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ FQLOCK(fq); -+ if (unlikely((fq_isset(fq, QMAN_FQ_STATE_CHANGING)) || -+ (fq->state == qman_fq_state_retired) || -+ (fq->state == qman_fq_state_oos))) { -+ rval = -EBUSY; -+ goto out; -+ } -+ rval = table_push_fq(p, fq); -+ if (rval) -+ goto out; -+ mcc = qm_mc_start(&p->p); -+ mcc->alterfq.fqid = cpu_to_be32(fq->fqid); -+ qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_RETIRE); -+ res = mcr->result; -+ /* "Elegant" would be to treat OK/PENDING the same way; set CHANGING, -+ * and defer the flags until FQRNI or FQRN (respectively) show up. But -+ * "Friendly" is to process OK immediately, and not set CHANGING. We do -+ * friendly, otherwise the caller doesn't necessarily have a fully -+ * "retired" FQ on return even if the retirement was immediate. However -+ * this does mean some code duplication between here and -+ * fq_state_change(). */ -+ if (likely(res == QM_MCR_RESULT_OK)) { -+ rval = 0; -+ /* Process 'fq' right away, we'll ignore FQRNI */ -+ if (mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY) -+ fq_set(fq, QMAN_FQ_STATE_NE); -+ if (mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT) -+ fq_set(fq, QMAN_FQ_STATE_ORL); -+ else -+ table_del_fq(p, fq); -+ if (flags) -+ *flags = fq->flags; -+ fq->state = qman_fq_state_retired; -+ if (fq->cb.fqs) { -+ /* Another issue with supporting "immediate" retirement -+ * is that we're forced to drop FQRNIs, because by the -+ * time they're seen it may already be "too late" (the -+ * fq may have been OOS'd and free()'d already). But if -+ * the upper layer wants a callback whether it's -+ * immediate or not, we have to fake a "MR" entry to -+ * look like an FQRNI... */ -+ struct qm_mr_entry msg; -+ msg.verb = QM_MR_VERB_FQRNI; -+ msg.fq.fqs = mcr->alterfq.fqs; -+ msg.fq.fqid = fq->fqid; -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ msg.fq.contextB = fq->key; -+#else -+ msg.fq.contextB = (u32)(uintptr_t)fq; -+#endif -+ fq->cb.fqs(p, fq, &msg); -+ } -+ } else if (res == QM_MCR_RESULT_PENDING) { -+ rval = 1; -+ fq_set(fq, QMAN_FQ_STATE_CHANGING); -+ } else { -+ rval = -EIO; -+ table_del_fq(p, fq); -+ } -+out: -+ FQUNLOCK(fq); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return rval; -+} -+EXPORT_SYMBOL(qman_retire_fq); -+ -+int qman_oos_fq(struct qman_fq *fq) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ int ret = 0; -+ u8 res; -+ -+ if (fq->state != qman_fq_state_retired) -+ return -EINVAL; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))) -+ return -EINVAL; -+#endif -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ FQLOCK(fq); -+ if (unlikely((fq_isset(fq, QMAN_FQ_STATE_BLOCKOOS)) || -+ (fq->state != qman_fq_state_retired))) { -+ ret = -EBUSY; -+ goto out; -+ } -+ mcc = qm_mc_start(&p->p); -+ mcc->alterfq.fqid = cpu_to_be32(fq->fqid); -+ qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_OOS); -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ ret = -EIO; -+ goto out; -+ } -+ fq->state = qman_fq_state_oos; -+out: -+ FQUNLOCK(fq); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_oos_fq); -+ -+int qman_fq_flow_control(struct qman_fq *fq, int xon) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ int ret = 0; -+ u8 res; -+ u8 myverb; -+ -+ if ((fq->state == qman_fq_state_oos) || -+ (fq->state == qman_fq_state_retired) || -+ (fq->state == qman_fq_state_parked)) -+ return -EINVAL; -+ -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))) -+ return -EINVAL; -+#endif -+ /* Issue a ALTER_FQXON or ALTER_FQXOFF management command */ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ FQLOCK(fq); -+ if (unlikely((fq_isset(fq, QMAN_FQ_STATE_CHANGING)) || -+ (fq->state == qman_fq_state_parked) || -+ (fq->state == qman_fq_state_oos) || -+ (fq->state == qman_fq_state_retired))) { -+ ret = -EBUSY; -+ goto out; -+ } -+ mcc = qm_mc_start(&p->p); -+ mcc->alterfq.fqid = fq->fqid; -+ mcc->alterfq.count = 0; -+ myverb = xon ? QM_MCC_VERB_ALTER_FQXON : QM_MCC_VERB_ALTER_FQXOFF; -+ -+ qm_mc_commit(&p->p, myverb); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == myverb); -+ -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ ret = -EIO; -+ goto out; -+ } -+out: -+ FQUNLOCK(fq); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_fq_flow_control); -+ -+int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p = get_affine_portal(); -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ mcc = qm_mc_start(&p->p); -+ mcc->queryfq.fqid = cpu_to_be32(fq->fqid); -+ qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) -+ *fqd = mcr->queryfq.fqd; -+ hw_fqd_to_cpu(fqd); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (res != QM_MCR_RESULT_OK) -+ return -EIO; -+ return 0; -+} -+EXPORT_SYMBOL(qman_query_fq); -+ -+int qman_query_fq_np(struct qman_fq *fq, struct qm_mcr_queryfq_np *np) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p = get_affine_portal(); -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ mcc = qm_mc_start(&p->p); -+ mcc->queryfq.fqid = cpu_to_be32(fq->fqid); -+ qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) { -+ *np = mcr->queryfq_np; -+ np->fqd_link = be24_to_cpu(np->fqd_link); -+ np->odp_seq = be16_to_cpu(np->odp_seq); -+ np->orp_nesn = be16_to_cpu(np->orp_nesn); -+ np->orp_ea_hseq = be16_to_cpu(np->orp_ea_hseq); -+ np->orp_ea_tseq = be16_to_cpu(np->orp_ea_tseq); -+ np->orp_ea_hptr = be24_to_cpu(np->orp_ea_hptr); -+ np->orp_ea_tptr = be24_to_cpu(np->orp_ea_tptr); -+ np->pfdr_hptr = be24_to_cpu(np->pfdr_hptr); -+ np->pfdr_tptr = be24_to_cpu(np->pfdr_tptr); -+ np->ics_surp = be16_to_cpu(np->ics_surp); -+ np->byte_cnt = be32_to_cpu(np->byte_cnt); -+ np->frm_cnt = be24_to_cpu(np->frm_cnt); -+ np->ra1_sfdr = be16_to_cpu(np->ra1_sfdr); -+ np->ra2_sfdr = be16_to_cpu(np->ra2_sfdr); -+ np->od1_sfdr = be16_to_cpu(np->od1_sfdr); -+ np->od2_sfdr = be16_to_cpu(np->od2_sfdr); -+ np->od3_sfdr = be16_to_cpu(np->od3_sfdr); -+ } -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (res == QM_MCR_RESULT_ERR_FQID) -+ return -ERANGE; -+ else if (res != QM_MCR_RESULT_OK) -+ return -EIO; -+ return 0; -+} -+EXPORT_SYMBOL(qman_query_fq_np); -+ -+int qman_query_wq(u8 query_dedicated, struct qm_mcr_querywq *wq) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p = get_affine_portal(); -+ unsigned long irqflags __maybe_unused; -+ u8 res, myverb; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ myverb = (query_dedicated) ? QM_MCR_VERB_QUERYWQ_DEDICATED : -+ QM_MCR_VERB_QUERYWQ; -+ mcc = qm_mc_start(&p->p); -+ mcc->querywq.channel.id = cpu_to_be16(wq->channel.id); -+ qm_mc_commit(&p->p, myverb); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == myverb); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) { -+ int i, array_len; -+ wq->channel.id = be16_to_cpu(mcr->querywq.channel.id); -+ array_len = ARRAY_SIZE(mcr->querywq.wq_len); -+ for (i = 0; i < array_len; i++) -+ wq->wq_len[i] = be32_to_cpu(mcr->querywq.wq_len[i]); -+ } -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("QUERYWQ failed: %s\n", mcr_result_str(res)); -+ return -EIO; -+ } -+ return 0; -+} -+EXPORT_SYMBOL(qman_query_wq); -+ -+int qman_testwrite_cgr(struct qman_cgr *cgr, u64 i_bcnt, -+ struct qm_mcr_cgrtestwrite *result) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p = get_affine_portal(); -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ mcc = qm_mc_start(&p->p); -+ mcc->cgrtestwrite.cgid = cgr->cgrid; -+ mcc->cgrtestwrite.i_bcnt_hi = (u8)(i_bcnt >> 32); -+ mcc->cgrtestwrite.i_bcnt_lo = (u32)i_bcnt; -+ qm_mc_commit(&p->p, QM_MCC_VERB_CGRTESTWRITE); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_CGRTESTWRITE); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) -+ *result = mcr->cgrtestwrite; -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CGR TEST WRITE failed: %s\n", mcr_result_str(res)); -+ return -EIO; -+ } -+ return 0; -+} -+EXPORT_SYMBOL(qman_testwrite_cgr); -+ -+int qman_query_cgr(struct qman_cgr *cgr, struct qm_mcr_querycgr *cgrd) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p = get_affine_portal(); -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ int i; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ mcc = qm_mc_start(&p->p); -+ mcc->querycgr.cgid = cgr->cgrid; -+ qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCGR); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_QUERYCGR); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) -+ *cgrd = mcr->querycgr; -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("QUERY_CGR failed: %s\n", mcr_result_str(res)); -+ return -EIO; -+ } -+ cgrd->cgr.wr_parm_g.word = -+ be32_to_cpu(cgrd->cgr.wr_parm_g.word); -+ cgrd->cgr.wr_parm_y.word = -+ be32_to_cpu(cgrd->cgr.wr_parm_y.word); -+ cgrd->cgr.wr_parm_r.word = -+ be32_to_cpu(cgrd->cgr.wr_parm_r.word); -+ cgrd->cgr.cscn_targ = be32_to_cpu(cgrd->cgr.cscn_targ); -+ cgrd->cgr.__cs_thres = be16_to_cpu(cgrd->cgr.__cs_thres); -+ for (i = 0; i < ARRAY_SIZE(cgrd->cscn_targ_swp); i++) -+ be32_to_cpus(&cgrd->cscn_targ_swp[i]); -+ return 0; -+} -+EXPORT_SYMBOL(qman_query_cgr); -+ -+int qman_query_congestion(struct qm_mcr_querycongestion *congestion) -+{ -+ struct qm_mc_result *mcr; -+ struct qman_portal *p = get_affine_portal(); -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ int i; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ qm_mc_start(&p->p); -+ qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCONGESTION); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_MCC_VERB_QUERYCONGESTION); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) -+ memcpy_fromio(congestion, &mcr->querycongestion, -+ sizeof(*congestion)); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("QUERY_CONGESTION failed: %s\n", mcr_result_str(res)); -+ return -EIO; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(congestion->state.__state); i++) -+ be32_to_cpus(&congestion->state.__state[i]); -+ return 0; -+} -+EXPORT_SYMBOL(qman_query_congestion); -+ -+/* internal function used as a wait_event() expression */ -+static int set_p_vdqcr(struct qman_portal *p, struct qman_fq *fq, u32 vdqcr) -+{ -+ unsigned long irqflags __maybe_unused; -+ int ret = -EBUSY; -+ PORTAL_IRQ_LOCK(p, irqflags); -+ if (!p->vdqcr_owned) { -+ FQLOCK(fq); -+ if (fq_isset(fq, QMAN_FQ_STATE_VDQCR)) -+ goto escape; -+ fq_set(fq, QMAN_FQ_STATE_VDQCR); -+ FQUNLOCK(fq); -+ p->vdqcr_owned = fq; -+ ret = 0; -+ } -+escape: -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ if (!ret) -+ qm_dqrr_vdqcr_set(&p->p, vdqcr); -+ return ret; -+} -+ -+static int set_vdqcr(struct qman_portal **p, struct qman_fq *fq, u32 vdqcr) -+{ -+ int ret; -+ *p = get_affine_portal(); -+ ret = set_p_vdqcr(*p, fq, vdqcr); -+ put_affine_portal(); -+ return ret; -+} -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+static int wait_p_vdqcr_start(struct qman_portal *p, struct qman_fq *fq, -+ u32 vdqcr, u32 flags) -+{ -+ int ret = 0; -+ if (flags & QMAN_VOLATILE_FLAG_WAIT_INT) -+ ret = wait_event_interruptible(affine_queue, -+ !(ret = set_p_vdqcr(p, fq, vdqcr))); -+ else -+ wait_event(affine_queue, !(ret = set_p_vdqcr(p, fq, vdqcr))); -+ return ret; -+} -+ -+static int wait_vdqcr_start(struct qman_portal **p, struct qman_fq *fq, -+ u32 vdqcr, u32 flags) -+{ -+ int ret = 0; -+ if (flags & QMAN_VOLATILE_FLAG_WAIT_INT) -+ ret = wait_event_interruptible(affine_queue, -+ !(ret = set_vdqcr(p, fq, vdqcr))); -+ else -+ wait_event(affine_queue, !(ret = set_vdqcr(p, fq, vdqcr))); -+ return ret; -+} -+#endif -+ -+int qman_p_volatile_dequeue(struct qman_portal *p, struct qman_fq *fq, -+ u32 flags __maybe_unused, u32 vdqcr) -+{ -+ int ret; -+ -+ if ((fq->state != qman_fq_state_parked) && -+ (fq->state != qman_fq_state_retired)) -+ return -EINVAL; -+ if (vdqcr & QM_VDQCR_FQID_MASK) -+ return -EINVAL; -+ if (fq_isset(fq, QMAN_FQ_STATE_VDQCR)) -+ return -EBUSY; -+ vdqcr = (vdqcr & ~QM_VDQCR_FQID_MASK) | fq->fqid; -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_VOLATILE_FLAG_WAIT) -+ ret = wait_p_vdqcr_start(p, fq, vdqcr, flags); -+ else -+#endif -+ ret = set_p_vdqcr(p, fq, vdqcr); -+ if (ret) -+ return ret; -+ /* VDQCR is set */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_VOLATILE_FLAG_FINISH) { -+ if (flags & QMAN_VOLATILE_FLAG_WAIT_INT) -+ /* NB: don't propagate any error - the caller wouldn't -+ * know whether the VDQCR was issued or not. A signal -+ * could arrive after returning anyway, so the caller -+ * can check signal_pending() if that's an issue. */ -+ wait_event_interruptible(affine_queue, -+ !fq_isset(fq, QMAN_FQ_STATE_VDQCR)); -+ else -+ wait_event(affine_queue, -+ !fq_isset(fq, QMAN_FQ_STATE_VDQCR)); -+ } -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_p_volatile_dequeue); -+ -+int qman_volatile_dequeue(struct qman_fq *fq, u32 flags __maybe_unused, -+ u32 vdqcr) -+{ -+ struct qman_portal *p; -+ int ret; -+ -+ if ((fq->state != qman_fq_state_parked) && -+ (fq->state != qman_fq_state_retired)) -+ return -EINVAL; -+ if (vdqcr & QM_VDQCR_FQID_MASK) -+ return -EINVAL; -+ if (fq_isset(fq, QMAN_FQ_STATE_VDQCR)) -+ return -EBUSY; -+ vdqcr = (vdqcr & ~QM_VDQCR_FQID_MASK) | fq->fqid; -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_VOLATILE_FLAG_WAIT) -+ ret = wait_vdqcr_start(&p, fq, vdqcr, flags); -+ else -+#endif -+ ret = set_vdqcr(&p, fq, vdqcr); -+ if (ret) -+ return ret; -+ /* VDQCR is set */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_VOLATILE_FLAG_FINISH) { -+ if (flags & QMAN_VOLATILE_FLAG_WAIT_INT) -+ /* NB: don't propagate any error - the caller wouldn't -+ * know whether the VDQCR was issued or not. A signal -+ * could arrive after returning anyway, so the caller -+ * can check signal_pending() if that's an issue. */ -+ wait_event_interruptible(affine_queue, -+ !fq_isset(fq, QMAN_FQ_STATE_VDQCR)); -+ else -+ wait_event(affine_queue, -+ !fq_isset(fq, QMAN_FQ_STATE_VDQCR)); -+ } -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_volatile_dequeue); -+ -+static noinline void update_eqcr_ci(struct qman_portal *p, u8 avail) -+{ -+ if (avail) -+ qm_eqcr_cce_prefetch(&p->p); -+ else -+ qm_eqcr_cce_update(&p->p); -+} -+ -+int qman_eqcr_is_empty(void) -+{ -+ unsigned long irqflags __maybe_unused; -+ struct qman_portal *p = get_affine_portal(); -+ u8 avail; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ update_eqcr_ci(p, 0); -+ avail = qm_eqcr_get_fill(&p->p); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return avail == 0; -+} -+EXPORT_SYMBOL(qman_eqcr_is_empty); -+ -+void qman_set_dc_ern(qman_cb_dc_ern handler, int affine) -+{ -+ if (affine) { -+ unsigned long irqflags __maybe_unused; -+ struct qman_portal *p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ p->cb_dc_ern = handler; -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ } else -+ cb_dc_ern = handler; -+} -+EXPORT_SYMBOL(qman_set_dc_ern); -+ -+static inline struct qm_eqcr_entry *try_p_eq_start(struct qman_portal *p, -+ unsigned long *irqflags __maybe_unused, -+ struct qman_fq *fq, -+ const struct qm_fd *fd, -+ u32 flags) -+{ -+ struct qm_eqcr_entry *eq; -+ u8 avail; -+ PORTAL_IRQ_LOCK(p, (*irqflags)); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) && -+ (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) { -+ if (p->eqci_owned) { -+ PORTAL_IRQ_UNLOCK(p, (*irqflags)); -+ return NULL; -+ } -+ p->eqci_owned = fq; -+ } -+#endif -+ if (p->use_eqcr_ci_stashing) { -+ /* -+ * The stashing case is easy, only update if we need to in -+ * order to try and liberate ring entries. -+ */ -+ eq = qm_eqcr_start_stash(&p->p); -+ } else { -+ /* -+ * The non-stashing case is harder, need to prefetch ahead of -+ * time. -+ */ -+ avail = qm_eqcr_get_avail(&p->p); -+ if (avail < 2) -+ update_eqcr_ci(p, avail); -+ eq = qm_eqcr_start_no_stash(&p->p); -+ } -+ -+ if (unlikely(!eq)) { -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) && -+ (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) -+ p->eqci_owned = NULL; -+#endif -+ PORTAL_IRQ_UNLOCK(p, (*irqflags)); -+ return NULL; -+ } -+ if (flags & QMAN_ENQUEUE_FLAG_DCA) -+ eq->dca = QM_EQCR_DCA_ENABLE | -+ ((flags & QMAN_ENQUEUE_FLAG_DCA_PARK) ? -+ QM_EQCR_DCA_PARK : 0) | -+ ((flags >> 8) & QM_EQCR_DCA_IDXMASK); -+ eq->fqid = cpu_to_be32(fq->fqid); -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ eq->tag = cpu_to_be32(fq->key); -+#else -+ eq->tag = cpu_to_be32((u32)(uintptr_t)fq); -+#endif -+ eq->fd = *fd; -+ cpu_to_hw_fd(&eq->fd); -+ return eq; -+} -+ -+static inline struct qm_eqcr_entry *try_eq_start(struct qman_portal **p, -+ unsigned long *irqflags __maybe_unused, -+ struct qman_fq *fq, -+ const struct qm_fd *fd, -+ u32 flags) -+{ -+ struct qm_eqcr_entry *eq; -+ *p = get_affine_portal(); -+ eq = try_p_eq_start(*p, irqflags, fq, fd, flags); -+ if (!eq) -+ put_affine_portal(); -+ return eq; -+} -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+static noinline struct qm_eqcr_entry *__wait_eq_start(struct qman_portal **p, -+ unsigned long *irqflags __maybe_unused, -+ struct qman_fq *fq, -+ const struct qm_fd *fd, -+ u32 flags) -+{ -+ struct qm_eqcr_entry *eq = try_eq_start(p, irqflags, fq, fd, flags); -+ if (!eq) -+ qm_eqcr_set_ithresh(&(*p)->p, EQCR_ITHRESH); -+ return eq; -+} -+static noinline struct qm_eqcr_entry *wait_eq_start(struct qman_portal **p, -+ unsigned long *irqflags __maybe_unused, -+ struct qman_fq *fq, -+ const struct qm_fd *fd, -+ u32 flags) -+{ -+ struct qm_eqcr_entry *eq; -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT) -+ /* NB: return NULL if signal occurs before completion. Signal -+ * can occur during return. Caller must check for signal */ -+ wait_event_interruptible(affine_queue, -+ (eq = __wait_eq_start(p, irqflags, fq, fd, flags))); -+ else -+ wait_event(affine_queue, -+ (eq = __wait_eq_start(p, irqflags, fq, fd, flags))); -+ return eq; -+} -+static noinline struct qm_eqcr_entry *__wait_p_eq_start(struct qman_portal *p, -+ unsigned long *irqflags __maybe_unused, -+ struct qman_fq *fq, -+ const struct qm_fd *fd, -+ u32 flags) -+{ -+ struct qm_eqcr_entry *eq = try_p_eq_start(p, irqflags, fq, fd, flags); -+ if (!eq) -+ qm_eqcr_set_ithresh(&p->p, EQCR_ITHRESH); -+ return eq; -+} -+static noinline struct qm_eqcr_entry *wait_p_eq_start(struct qman_portal *p, -+ unsigned long *irqflags __maybe_unused, -+ struct qman_fq *fq, -+ const struct qm_fd *fd, -+ u32 flags) -+{ -+ struct qm_eqcr_entry *eq; -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT) -+ /* NB: return NULL if signal occurs before completion. Signal -+ * can occur during return. Caller must check for signal */ -+ wait_event_interruptible(affine_queue, -+ (eq = __wait_p_eq_start(p, irqflags, fq, fd, flags))); -+ else -+ wait_event(affine_queue, -+ (eq = __wait_p_eq_start(p, irqflags, fq, fd, flags))); -+ return eq; -+} -+#endif -+ -+int qman_p_enqueue(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_fd *fd, u32 flags) -+{ -+ struct qm_eqcr_entry *eq; -+ unsigned long irqflags __maybe_unused; -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT) -+ eq = wait_p_eq_start(p, &irqflags, fq, fd, flags); -+ else -+#endif -+ eq = try_p_eq_start(p, &irqflags, fq, fd, flags); -+ if (!eq) -+ return -EBUSY; -+ /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */ -+ qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE | -+ (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT))); -+ /* Factor the below out, it's used from qman_enqueue_orp() too */ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) && -+ (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) { -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT) -+ /* NB: return success even if signal occurs before -+ * condition is true. pvb_commit guarantees success */ -+ wait_event_interruptible(affine_queue, -+ (p->eqci_owned != fq)); -+ else -+ wait_event(affine_queue, (p->eqci_owned != fq)); -+ } -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_p_enqueue); -+ -+int qman_enqueue(struct qman_fq *fq, const struct qm_fd *fd, u32 flags) -+{ -+ struct qman_portal *p; -+ struct qm_eqcr_entry *eq; -+ unsigned long irqflags __maybe_unused; -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT) -+ eq = wait_eq_start(&p, &irqflags, fq, fd, flags); -+ else -+#endif -+ eq = try_eq_start(&p, &irqflags, fq, fd, flags); -+ if (!eq) -+ return -EBUSY; -+ /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */ -+ qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE | -+ (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT))); -+ /* Factor the below out, it's used from qman_enqueue_orp() too */ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) && -+ (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) { -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT) -+ /* NB: return success even if signal occurs before -+ * condition is true. pvb_commit guarantees success */ -+ wait_event_interruptible(affine_queue, -+ (p->eqci_owned != fq)); -+ else -+ wait_event(affine_queue, (p->eqci_owned != fq)); -+ } -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_enqueue); -+ -+int qman_p_enqueue_orp(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_fd *fd, u32 flags, -+ struct qman_fq *orp, u16 orp_seqnum) -+{ -+ struct qm_eqcr_entry *eq; -+ unsigned long irqflags __maybe_unused; -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT) -+ eq = wait_p_eq_start(p, &irqflags, fq, fd, flags); -+ else -+#endif -+ eq = try_p_eq_start(p, &irqflags, fq, fd, flags); -+ if (!eq) -+ return -EBUSY; -+ /* Process ORP-specifics here */ -+ if (flags & QMAN_ENQUEUE_FLAG_NLIS) -+ orp_seqnum |= QM_EQCR_SEQNUM_NLIS; -+ else { -+ orp_seqnum &= ~QM_EQCR_SEQNUM_NLIS; -+ if (flags & QMAN_ENQUEUE_FLAG_NESN) -+ orp_seqnum |= QM_EQCR_SEQNUM_NESN; -+ else -+ /* No need to check 4 QMAN_ENQUEUE_FLAG_HOLE */ -+ orp_seqnum &= ~QM_EQCR_SEQNUM_NESN; -+ } -+ eq->seqnum = cpu_to_be16(orp_seqnum); -+ eq->orp = cpu_to_be32(orp->fqid); -+ /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */ -+ qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_ORP | -+ ((flags & (QMAN_ENQUEUE_FLAG_HOLE | QMAN_ENQUEUE_FLAG_NESN)) ? -+ 0 : QM_EQCR_VERB_CMD_ENQUEUE) | -+ (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT))); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) && -+ (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) { -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT) -+ /* NB: return success even if signal occurs before -+ * condition is true. pvb_commit guarantees success */ -+ wait_event_interruptible(affine_queue, -+ (p->eqci_owned != fq)); -+ else -+ wait_event(affine_queue, (p->eqci_owned != fq)); -+ } -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_p_enqueue_orp); -+ -+int qman_enqueue_orp(struct qman_fq *fq, const struct qm_fd *fd, u32 flags, -+ struct qman_fq *orp, u16 orp_seqnum) -+{ -+ struct qman_portal *p; -+ struct qm_eqcr_entry *eq; -+ unsigned long irqflags __maybe_unused; -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT) -+ eq = wait_eq_start(&p, &irqflags, fq, fd, flags); -+ else -+#endif -+ eq = try_eq_start(&p, &irqflags, fq, fd, flags); -+ if (!eq) -+ return -EBUSY; -+ /* Process ORP-specifics here */ -+ if (flags & QMAN_ENQUEUE_FLAG_NLIS) -+ orp_seqnum |= QM_EQCR_SEQNUM_NLIS; -+ else { -+ orp_seqnum &= ~QM_EQCR_SEQNUM_NLIS; -+ if (flags & QMAN_ENQUEUE_FLAG_NESN) -+ orp_seqnum |= QM_EQCR_SEQNUM_NESN; -+ else -+ /* No need to check 4 QMAN_ENQUEUE_FLAG_HOLE */ -+ orp_seqnum &= ~QM_EQCR_SEQNUM_NESN; -+ } -+ eq->seqnum = cpu_to_be16(orp_seqnum); -+ eq->orp = cpu_to_be32(orp->fqid); -+ /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */ -+ qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_ORP | -+ ((flags & (QMAN_ENQUEUE_FLAG_HOLE | QMAN_ENQUEUE_FLAG_NESN)) ? -+ 0 : QM_EQCR_VERB_CMD_ENQUEUE) | -+ (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT))); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) && -+ (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) { -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT) -+ /* NB: return success even if signal occurs before -+ * condition is true. pvb_commit guarantees success */ -+ wait_event_interruptible(affine_queue, -+ (p->eqci_owned != fq)); -+ else -+ wait_event(affine_queue, (p->eqci_owned != fq)); -+ } -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_enqueue_orp); -+ -+int qman_p_enqueue_precommit(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_fd *fd, u32 flags, -+ qman_cb_precommit cb, void *cb_arg) -+{ -+ struct qm_eqcr_entry *eq; -+ unsigned long irqflags __maybe_unused; -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT) -+ eq = wait_p_eq_start(p, &irqflags, fq, fd, flags); -+ else -+#endif -+ eq = try_p_eq_start(p, &irqflags, fq, fd, flags); -+ if (!eq) -+ return -EBUSY; -+ /* invoke user supplied callback function before writing commit verb */ -+ if (cb(cb_arg)) { -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ return -EINVAL; -+ } -+ /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */ -+ qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE | -+ (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT))); -+ /* Factor the below out, it's used from qman_enqueue_orp() too */ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) && -+ (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) { -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT) -+ /* NB: return success even if signal occurs before -+ * condition is true. pvb_commit guarantees success */ -+ wait_event_interruptible(affine_queue, -+ (p->eqci_owned != fq)); -+ else -+ wait_event(affine_queue, (p->eqci_owned != fq)); -+ } -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_p_enqueue_precommit); -+ -+int qman_enqueue_precommit(struct qman_fq *fq, const struct qm_fd *fd, -+ u32 flags, qman_cb_precommit cb, void *cb_arg) -+{ -+ struct qman_portal *p; -+ struct qm_eqcr_entry *eq; -+ unsigned long irqflags __maybe_unused; -+ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT) -+ eq = wait_eq_start(&p, &irqflags, fq, fd, flags); -+ else -+#endif -+ eq = try_eq_start(&p, &irqflags, fq, fd, flags); -+ if (!eq) -+ return -EBUSY; -+ /* invoke user supplied callback function before writing commit verb */ -+ if (cb(cb_arg)) { -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return -EINVAL; -+ } -+ /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */ -+ qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE | -+ (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT))); -+ /* Factor the below out, it's used from qman_enqueue_orp() too */ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+ if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) && -+ (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) { -+ if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT) -+ /* NB: return success even if signal occurs before -+ * condition is true. pvb_commit guarantees success */ -+ wait_event_interruptible(affine_queue, -+ (p->eqci_owned != fq)); -+ else -+ wait_event(affine_queue, (p->eqci_owned != fq)); -+ } -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_enqueue_precommit); -+ -+int qman_modify_cgr(struct qman_cgr *cgr, u32 flags, -+ struct qm_mcc_initcgr *opts) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p = get_affine_portal(); -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ u8 verb = QM_MCC_VERB_MODIFYCGR; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ mcc = qm_mc_start(&p->p); -+ if (opts) -+ mcc->initcgr = *opts; -+ mcc->initcgr.we_mask = cpu_to_be16(mcc->initcgr.we_mask); -+ mcc->initcgr.cgr.wr_parm_g.word = -+ cpu_to_be32(mcc->initcgr.cgr.wr_parm_g.word); -+ mcc->initcgr.cgr.wr_parm_y.word = -+ cpu_to_be32(mcc->initcgr.cgr.wr_parm_y.word); -+ mcc->initcgr.cgr.wr_parm_r.word = -+ cpu_to_be32(mcc->initcgr.cgr.wr_parm_r.word); -+ mcc->initcgr.cgr.cscn_targ = cpu_to_be32(mcc->initcgr.cgr.cscn_targ); -+ mcc->initcgr.cgr.__cs_thres = cpu_to_be16(mcc->initcgr.cgr.__cs_thres); -+ -+ mcc->initcgr.cgid = cgr->cgrid; -+ if (flags & QMAN_CGR_FLAG_USE_INIT) -+ verb = QM_MCC_VERB_INITCGR; -+ qm_mc_commit(&p->p, verb); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == verb); -+ res = mcr->result; -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return (res == QM_MCR_RESULT_OK) ? 0 : -EIO; -+} -+EXPORT_SYMBOL(qman_modify_cgr); -+ -+#define TARG_MASK(n) (0x80000000 >> (n->config->public_cfg.channel - \ -+ QM_CHANNEL_SWPORTAL0)) -+#define TARG_DCP_MASK(n) (0x80000000 >> (10 + n)) -+#define PORTAL_IDX(n) (n->config->public_cfg.channel - QM_CHANNEL_SWPORTAL0) -+ -+static u8 qman_cgr_cpus[__CGR_NUM]; -+ -+int qman_create_cgr(struct qman_cgr *cgr, u32 flags, -+ struct qm_mcc_initcgr *opts) -+{ -+ unsigned long irqflags __maybe_unused; -+ struct qm_mcr_querycgr cgr_state; -+ struct qm_mcc_initcgr local_opts; -+ int ret; -+ struct qman_portal *p; -+ -+ /* We have to check that the provided CGRID is within the limits of the -+ * data-structures, for obvious reasons. However we'll let h/w take -+ * care of determining whether it's within the limits of what exists on -+ * the SoC. */ -+ if (cgr->cgrid >= __CGR_NUM) -+ return -EINVAL; -+ -+ preempt_disable(); -+ p = get_affine_portal(); -+ qman_cgr_cpus[cgr->cgrid] = smp_processor_id(); -+ preempt_enable(); -+ -+ memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr)); -+ cgr->chan = p->config->public_cfg.channel; -+ spin_lock_irqsave(&p->cgr_lock, irqflags); -+ -+ /* if no opts specified, just add it to the list */ -+ if (!opts) -+ goto add_list; -+ -+ ret = qman_query_cgr(cgr, &cgr_state); -+ if (ret) -+ goto release_lock; -+ if (opts) -+ local_opts = *opts; -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) -+ local_opts.cgr.cscn_targ_upd_ctrl = -+ QM_CGR_TARG_UDP_CTRL_WRITE_BIT | PORTAL_IDX(p); -+ else -+ /* Overwrite TARG */ -+ local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ | -+ TARG_MASK(p); -+ local_opts.we_mask |= QM_CGR_WE_CSCN_TARG; -+ -+ /* send init if flags indicate so */ -+ if (opts && (flags & QMAN_CGR_FLAG_USE_INIT)) -+ ret = qman_modify_cgr(cgr, QMAN_CGR_FLAG_USE_INIT, &local_opts); -+ else -+ ret = qman_modify_cgr(cgr, 0, &local_opts); -+ if (ret) -+ goto release_lock; -+add_list: -+ list_add(&cgr->node, &p->cgr_cbs); -+ -+ /* Determine if newly added object requires its callback to be called */ -+ ret = qman_query_cgr(cgr, &cgr_state); -+ if (ret) { -+ /* we can't go back, so proceed and return success, but screen -+ * and wail to the log file */ -+ pr_crit("CGR HW state partially modified\n"); -+ ret = 0; -+ goto release_lock; -+ } -+ if (cgr->cb && cgr_state.cgr.cscn_en && qman_cgrs_get(&p->cgrs[1], -+ cgr->cgrid)) -+ cgr->cb(p, cgr, 1); -+release_lock: -+ spin_unlock_irqrestore(&p->cgr_lock, irqflags); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_create_cgr); -+ -+int qman_create_cgr_to_dcp(struct qman_cgr *cgr, u32 flags, u16 dcp_portal, -+ struct qm_mcc_initcgr *opts) -+{ -+ unsigned long irqflags __maybe_unused; -+ struct qm_mcc_initcgr local_opts; -+ struct qm_mcr_querycgr cgr_state; -+ int ret; -+ -+ if ((qman_ip_rev & 0xFF00) < QMAN_REV30) { -+ pr_warn("This QMan version doesn't support to send CSCN to DCP portal\n"); -+ return -EINVAL; -+ } -+ /* We have to check that the provided CGRID is within the limits of the -+ * data-structures, for obvious reasons. However we'll let h/w take -+ * care of determining whether it's within the limits of what exists on -+ * the SoC. -+ */ -+ if (cgr->cgrid >= __CGR_NUM) -+ return -EINVAL; -+ -+ ret = qman_query_cgr(cgr, &cgr_state); -+ if (ret) -+ return ret; -+ -+ memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr)); -+ if (opts) -+ local_opts = *opts; -+ -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) -+ local_opts.cgr.cscn_targ_upd_ctrl = -+ QM_CGR_TARG_UDP_CTRL_WRITE_BIT | -+ QM_CGR_TARG_UDP_CTRL_DCP | dcp_portal; -+ else -+ local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ | -+ TARG_DCP_MASK(dcp_portal); -+ local_opts.we_mask |= QM_CGR_WE_CSCN_TARG; -+ -+ /* send init if flags indicate so */ -+ if (opts && (flags & QMAN_CGR_FLAG_USE_INIT)) -+ ret = qman_modify_cgr(cgr, QMAN_CGR_FLAG_USE_INIT, -+ &local_opts); -+ else -+ ret = qman_modify_cgr(cgr, 0, &local_opts); -+ -+ return ret; -+} -+EXPORT_SYMBOL(qman_create_cgr_to_dcp); -+ -+int qman_delete_cgr(struct qman_cgr *cgr) -+{ -+ unsigned long irqflags __maybe_unused; -+ struct qm_mcr_querycgr cgr_state; -+ struct qm_mcc_initcgr local_opts; -+ int ret = 0; -+ struct qman_cgr *i; -+ struct qman_portal *p = get_affine_portal(); -+ -+ if (cgr->chan != p->config->public_cfg.channel) { -+ pr_crit("Attempting to delete cgr from different portal " -+ "than it was create: create 0x%x, delete 0x%x\n", -+ cgr->chan, p->config->public_cfg.channel); -+ ret = -EINVAL; -+ goto put_portal; -+ } -+ memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr)); -+ spin_lock_irqsave(&p->cgr_lock, irqflags); -+ list_del(&cgr->node); -+ /* -+ * If there are no other CGR objects for this CGRID in the list, update -+ * CSCN_TARG accordingly -+ */ -+ list_for_each_entry(i, &p->cgr_cbs, node) -+ if ((i->cgrid == cgr->cgrid) && i->cb) -+ goto release_lock; -+ ret = qman_query_cgr(cgr, &cgr_state); -+ if (ret) { -+ /* add back to the list */ -+ list_add(&cgr->node, &p->cgr_cbs); -+ goto release_lock; -+ } -+ /* Overwrite TARG */ -+ local_opts.we_mask = QM_CGR_WE_CSCN_TARG; -+ if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) -+ local_opts.cgr.cscn_targ_upd_ctrl = PORTAL_IDX(p); -+ else -+ local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ & -+ ~(TARG_MASK(p)); -+ ret = qman_modify_cgr(cgr, 0, &local_opts); -+ if (ret) -+ /* add back to the list */ -+ list_add(&cgr->node, &p->cgr_cbs); -+release_lock: -+ spin_unlock_irqrestore(&p->cgr_lock, irqflags); -+put_portal: -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_delete_cgr); -+ -+struct cgr_comp { -+ struct qman_cgr *cgr; -+ struct completion completion; -+}; -+ -+static int qman_delete_cgr_thread(void *p) -+{ -+ struct cgr_comp *cgr_comp = (struct cgr_comp *)p; -+ int res; -+ -+ res = qman_delete_cgr((struct qman_cgr *)cgr_comp->cgr); -+ complete(&cgr_comp->completion); -+ -+ return res; -+} -+ -+void qman_delete_cgr_safe(struct qman_cgr *cgr) -+{ -+ struct task_struct *thread; -+ struct cgr_comp cgr_comp; -+ -+ preempt_disable(); -+ if (qman_cgr_cpus[cgr->cgrid] != smp_processor_id()) { -+ init_completion(&cgr_comp.completion); -+ cgr_comp.cgr = cgr; -+ thread = kthread_create(qman_delete_cgr_thread, &cgr_comp, -+ "cgr_del"); -+ -+ if (likely(!IS_ERR(thread))) { -+ kthread_bind(thread, qman_cgr_cpus[cgr->cgrid]); -+ wake_up_process(thread); -+ wait_for_completion(&cgr_comp.completion); -+ preempt_enable(); -+ return; -+ } -+ } -+ qman_delete_cgr(cgr); -+ preempt_enable(); -+} -+EXPORT_SYMBOL(qman_delete_cgr_safe); -+ -+int qm_get_clock(u64 *clock_hz) -+{ -+ if (!qman_clk) { -+ pr_warn("Qman clock speed is unknown\n"); -+ return -EINVAL; -+ } -+ *clock_hz = (u64)qman_clk; -+ return 0; -+} -+EXPORT_SYMBOL(qm_get_clock); -+ -+int qm_set_clock(u64 clock_hz) -+{ -+ if (qman_clk) -+ return -1; -+ qman_clk = (u32)clock_hz; -+ return 0; -+} -+EXPORT_SYMBOL(qm_set_clock); -+ -+/* CEETM management command */ -+static int qman_ceetm_configure_lfqmt(struct qm_mcc_ceetm_lfqmt_config *opts) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->lfqmt_config = *opts; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_LFQMT_CONFIG); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_LFQMT_CONFIG); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: CONFIGURE LFQMT failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+ -+int qman_ceetm_query_lfqmt(int lfqid, -+ struct qm_mcr_ceetm_lfqmt_query *lfqmt_query) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->lfqmt_query.lfqid = lfqid; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_LFQMT_QUERY); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_LFQMT_QUERY); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) -+ *lfqmt_query = mcr->lfqmt_query; -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: QUERY LFQMT failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_query_lfqmt); -+ -+static int qman_ceetm_configure_cq(struct qm_mcc_ceetm_cq_config *opts) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->cq_config = *opts; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CQ_CONFIG); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ res = mcr->result; -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_CQ_CONFIG); -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: CONFIGURE CQ failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+ -+int qman_ceetm_query_cq(unsigned int cqid, unsigned int dcpid, -+ struct qm_mcr_ceetm_cq_query *cq_query) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->cq_query.cqid = cpu_to_be16(cqid); -+ mcc->cq_query.dcpid = dcpid; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CQ_QUERY); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_CQ_QUERY); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) { -+ *cq_query = mcr->cq_query; -+ hw_cq_query_to_cpu(cq_query); -+ } -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: QUERY CQ failed\n"); -+ return -EIO; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_query_cq); -+ -+static int qman_ceetm_configure_dct(struct qm_mcc_ceetm_dct_config *opts) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->dct_config = *opts; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_DCT_CONFIG); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_DCT_CONFIG); -+ res = mcr->result; -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: CONFIGURE DCT failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+ -+static int qman_ceetm_query_dct(struct qm_mcc_ceetm_dct_query *opts, -+ struct qm_mcr_ceetm_dct_query *dct_query) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p = get_affine_portal(); -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->dct_query = *opts; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_DCT_QUERY); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_DCT_QUERY); -+ res = mcr->result; -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: QUERY DCT failed\n"); -+ return -EIO; -+ } -+ -+ *dct_query = mcr->dct_query; -+ return 0; -+} -+ -+static int qman_ceetm_configure_class_scheduler( -+ struct qm_mcc_ceetm_class_scheduler_config *opts) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->csch_config = *opts; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG); -+ res = mcr->result; -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: CONFIGURE CLASS SCHEDULER failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+ -+static int qman_ceetm_query_class_scheduler(struct qm_ceetm_channel *channel, -+ struct qm_mcr_ceetm_class_scheduler_query *query) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->csch_query.cqcid = cpu_to_be16(channel->idx); -+ mcc->csch_query.dcpid = channel->dcp_idx; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CLASS_SCHEDULER_QUERY); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_CLASS_SCHEDULER_QUERY); -+ res = mcr->result; -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: QUERY CLASS SCHEDULER failed\n"); -+ return -EIO; -+ } -+ *query = mcr->csch_query; -+ return 0; -+} -+ -+static int qman_ceetm_configure_mapping_shaper_tcfc( -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config *opts) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->mst_config = *opts; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG); -+ res = mcr->result; -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: CONFIGURE CHANNEL MAPPING failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+ -+static int qman_ceetm_query_mapping_shaper_tcfc( -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query *opts, -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query *response) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->mst_query = *opts; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_MAPPING_SHAPER_TCFC_QUERY); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_MAPPING_SHAPER_TCFC_QUERY); -+ res = mcr->result; -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: QUERY CHANNEL MAPPING failed\n"); -+ return -EIO; -+ } -+ -+ *response = mcr->mst_query; -+ return 0; -+} -+ -+static int qman_ceetm_configure_ccgr(struct qm_mcc_ceetm_ccgr_config *opts) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->ccgr_config = *opts; -+ -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CCGR_CONFIG); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_CCGR_CONFIG); -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: CONFIGURE CCGR failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+ -+int qman_ceetm_query_ccgr(struct qm_mcc_ceetm_ccgr_query *ccgr_query, -+ struct qm_mcr_ceetm_ccgr_query *response) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->ccgr_query.ccgrid = cpu_to_be16(ccgr_query->ccgrid); -+ mcc->ccgr_query.dcpid = ccgr_query->dcpid; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CCGR_QUERY); -+ -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_CCGR_QUERY); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) { -+ *response = mcr->ccgr_query; -+ hw_ccgr_query_to_cpu(response); -+ } -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: QUERY CCGR failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_query_ccgr); -+ -+static int qman_ceetm_cq_peek_pop_xsfdrread(struct qm_ceetm_cq *cq, -+ u8 command_type, u16 xsfdr, -+ struct qm_mcr_ceetm_cq_peek_pop_xsfdrread *cq_ppxr) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ switch (command_type) { -+ case 0: -+ case 1: -+ mcc->cq_ppxr.cqid = (cq->parent->idx << 4) | cq->idx; -+ break; -+ case 2: -+ mcc->cq_ppxr.xsfdr = xsfdr; -+ break; -+ default: -+ break; -+ } -+ mcc->cq_ppxr.ct = command_type; -+ mcc->cq_ppxr.dcpid = cq->parent->dcp_idx; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CQ_PEEK_POP_XFDRREAD); -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_CQ_PEEK_POP_XFDRREAD); -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: CQ PEEK/POP/XSFDR READ failed\n"); -+ return -EIO; -+ } -+ *cq_ppxr = mcr->cq_ppxr; -+ return 0; -+} -+ -+static int qman_ceetm_query_statistics(u16 cid, -+ enum qm_dc_portal dcp_idx, -+ u16 command_type, -+ struct qm_mcr_ceetm_statistics_query *query_result) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->stats_query_write.cid = cid; -+ mcc->stats_query_write.dcpid = dcp_idx; -+ mcc->stats_query_write.ct = command_type; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_STATISTICS_QUERY_WRITE); -+ -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_STATISTICS_QUERY_WRITE); -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: STATISTICS QUERY failed\n"); -+ return -EIO; -+ } -+ *query_result = mcr->stats_query; -+ return 0; -+} -+ -+int qman_ceetm_query_write_statistics(u16 cid, enum qm_dc_portal dcp_idx, -+ u16 command_type, u64 frame_count, -+ u64 byte_count) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ mcc->stats_query_write.cid = cid; -+ mcc->stats_query_write.dcpid = dcp_idx; -+ mcc->stats_query_write.ct = command_type; -+ mcc->stats_query_write.frm_cnt = frame_count; -+ mcc->stats_query_write.byte_cnt = byte_count; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_STATISTICS_QUERY_WRITE); -+ -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_STATISTICS_QUERY_WRITE); -+ -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ -+ res = mcr->result; -+ if (res != QM_MCR_RESULT_OK) { -+ pr_err("CEETM: STATISTICS WRITE failed\n"); -+ return -EIO; -+ } -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_query_write_statistics); -+ -+int qman_ceetm_bps2tokenrate(u64 bps, struct qm_ceetm_rate *token_rate, -+ int rounding) -+{ -+ u16 pres; -+ u64 temp; -+ u64 qman_freq; -+ int ret; -+ -+ /* Read PRES from CEET_CFG_PRES register */ -+ ret = qman_ceetm_get_prescaler(&pres); -+ if (ret) -+ return -EINVAL; -+ -+ ret = qm_get_clock(&qman_freq); -+ if (ret) -+ return -EINVAL; -+ -+ /* token-rate = bytes-per-second * update-reference-period -+ * -+ * Where token-rate is N/8192 for a integer N, and -+ * update-reference-period is (2^22)/(PRES*QHz), where PRES -+ * is the prescalar value and QHz is the QMan clock frequency. -+ * So: -+ * -+ * token-rate = (byte-per-second*2^22)/PRES*QHZ) -+ * -+ * Converting to bits-per-second gives; -+ * -+ * token-rate = (bps*2^19) / (PRES*QHZ) -+ * N = (bps*2^32) / (PRES*QHz) -+ * -+ * And to avoid 64-bit overflow if 'bps' is larger than 4Gbps -+ * (yet minimise rounding error if 'bps' is small), we reorganise -+ * the formula to use two 16-bit shifts rather than 1 32-bit shift. -+ * N = (((bps*2^16)/PRES)*2^16)/QHz -+ */ -+ temp = ROUNDING((bps << 16), pres, rounding); -+ temp = ROUNDING((temp << 16), qman_freq, rounding); -+ token_rate->whole = temp >> 13; -+ token_rate->fraction = temp & (((u64)1 << 13) - 1); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_bps2tokenrate); -+ -+int qman_ceetm_tokenrate2bps(const struct qm_ceetm_rate *token_rate, u64 *bps, -+ int rounding) -+{ -+ u16 pres; -+ u64 temp; -+ u64 qman_freq; -+ int ret; -+ -+ /* Read PRES from CEET_CFG_PRES register */ -+ ret = qman_ceetm_get_prescaler(&pres); -+ if (ret) -+ return -EINVAL; -+ -+ ret = qm_get_clock(&qman_freq); -+ if (ret) -+ return -EINVAL; -+ -+ /* bytes-per-second = token-rate / update-reference-period -+ * -+ * where "token-rate" is N/8192 for an integer N, and -+ * "update-reference-period" is (2^22)/(PRES*QHz), where PRES is -+ * the prescalar value and QHz is the QMan clock frequency. So; -+ * -+ * bytes-per-second = (N/8192) / (4194304/PRES*QHz) -+ * = N*PRES*QHz / (4194304*8192) -+ * = N*PRES*QHz / (2^35) -+ * -+ * Converting to bits-per-second gives; -+ * -+ * bps = N*PRES*QHZ / (2^32) -+ * -+ * Note, the numerator has a maximum width of 72 bits! So to -+ * avoid 64-bit overflow errors, we calculate PRES*QHZ (maximum -+ * width 48 bits) divided by 2^9 (reducing to maximum 39 bits), before -+ * multiplying by N (goes to maximum of 63 bits). -+ * -+ * temp = PRES*QHZ / (2^16) -+ * kbps = temp*N / (2^16) -+ */ -+ temp = ROUNDING(qman_freq * pres, (u64)1 << 16 , rounding); -+ temp *= ((token_rate->whole << 13) + token_rate->fraction); -+ *bps = ROUNDING(temp, (u64)(1) << 16, rounding); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_tokenrate2bps); -+ -+int qman_ceetm_sp_claim(struct qm_ceetm_sp **sp, enum qm_dc_portal dcp_idx, -+ unsigned int sp_idx) -+{ -+ struct qm_ceetm_sp *p; -+ -+ DPA_ASSERT((dcp_idx == qm_dc_portal_fman0) || -+ (dcp_idx == qm_dc_portal_fman1)); -+ -+ if ((sp_idx < qman_ceetms[dcp_idx].sp_range[0]) || -+ (sp_idx >= (qman_ceetms[dcp_idx].sp_range[0] + -+ qman_ceetms[dcp_idx].sp_range[1]))) { -+ pr_err("Sub-portal index doesn't exist\n"); -+ return -EINVAL; -+ } -+ -+ list_for_each_entry(p, &qman_ceetms[dcp_idx].sub_portals, node) { -+ if ((p->idx == sp_idx) && (p->is_claimed == 0)) { -+ p->is_claimed = 1; -+ *sp = p; -+ return 0; -+ } -+ } -+ pr_err("The sub-portal#%d is not available!\n", sp_idx); -+ return -ENODEV; -+} -+EXPORT_SYMBOL(qman_ceetm_sp_claim); -+ -+int qman_ceetm_sp_release(struct qm_ceetm_sp *sp) -+{ -+ struct qm_ceetm_sp *p; -+ -+ if (sp->lni && sp->lni->is_claimed == 1) { -+ pr_err("The dependency of sub-portal has not been released!\n"); -+ return -EBUSY; -+ } -+ -+ list_for_each_entry(p, &qman_ceetms[sp->dcp_idx].sub_portals, node) { -+ if (p->idx == sp->idx) { -+ p->is_claimed = 0; -+ p->lni = NULL; -+ } -+ } -+ /* Disable CEETM mode of this sub-portal */ -+ qman_sp_disable_ceetm_mode(sp->dcp_idx, sp->idx); -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_sp_release); -+ -+int qman_ceetm_lni_claim(struct qm_ceetm_lni **lni, enum qm_dc_portal dcp_idx, -+ unsigned int lni_idx) -+{ -+ struct qm_ceetm_lni *p; -+ -+ if ((lni_idx < qman_ceetms[dcp_idx].lni_range[0]) || -+ (lni_idx >= (qman_ceetms[dcp_idx].lni_range[0] + -+ qman_ceetms[dcp_idx].lni_range[1]))) { -+ pr_err("The lni index is out of range\n"); -+ return -EINVAL; -+ } -+ -+ list_for_each_entry(p, &qman_ceetms[dcp_idx].lnis, node) { -+ if ((p->idx == lni_idx) && (p->is_claimed == 0)) { -+ *lni = p; -+ p->is_claimed = 1; -+ return 0; -+ } -+ } -+ -+ pr_err("The LNI#%d is not available!\n", lni_idx); -+ return -EINVAL; -+} -+EXPORT_SYMBOL(qman_ceetm_lni_claim); -+ -+int qman_ceetm_lni_release(struct qm_ceetm_lni *lni) -+{ -+ struct qm_ceetm_lni *p; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ -+ if (!list_empty(&lni->channels)) { -+ pr_err("The LNI dependencies are not released!\n"); -+ return -EBUSY; -+ } -+ -+ list_for_each_entry(p, &qman_ceetms[lni->dcp_idx].lnis, node) { -+ if (p->idx == lni->idx) { -+ p->shaper_enable = 0; -+ p->shaper_couple = 0; -+ p->cr_token_rate.whole = 0; -+ p->cr_token_rate.fraction = 0; -+ p->er_token_rate.whole = 0; -+ p->er_token_rate.fraction = 0; -+ p->cr_token_bucket_limit = 0; -+ p->er_token_bucket_limit = 0; -+ p->is_claimed = 0; -+ } -+ } -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ config_opts.dcpid = lni->dcp_idx; -+ memset(&config_opts.shaper_config, 0, -+ sizeof(config_opts.shaper_config)); -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_release); -+ -+int qman_ceetm_sp_set_lni(struct qm_ceetm_sp *sp, struct qm_ceetm_lni *lni) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_SP_MAPPING | sp->idx); -+ config_opts.dcpid = sp->dcp_idx; -+ config_opts.sp_mapping.map_lni_id = lni->idx; -+ sp->lni = lni; -+ -+ if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) -+ return -EINVAL; -+ -+ /* Enable CEETM mode for this sub-portal */ -+ return qman_sp_enable_ceetm_mode(sp->dcp_idx, sp->idx); -+} -+EXPORT_SYMBOL(qman_ceetm_sp_set_lni); -+ -+int qman_ceetm_sp_get_lni(struct qm_ceetm_sp *sp, unsigned int *lni_idx) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_SP_MAPPING | sp->idx); -+ query_opts.dcpid = sp->dcp_idx; -+ if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) { -+ pr_err("Can't get SP <-> LNI mapping\n"); -+ return -EINVAL; -+ } -+ *lni_idx = query_result.sp_mapping_query.map_lni_id; -+ sp->lni->idx = query_result.sp_mapping_query.map_lni_id; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_sp_get_lni); -+ -+int qman_ceetm_lni_enable_shaper(struct qm_ceetm_lni *lni, int coupled, -+ int oal) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ -+ if (lni->shaper_enable) { -+ pr_err("The shaper has already been enabled\n"); -+ return -EINVAL; -+ } -+ lni->shaper_enable = 1; -+ lni->shaper_couple = coupled; -+ lni->oal = oal; -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ config_opts.dcpid = lni->dcp_idx; -+ config_opts.shaper_config.cpl = coupled; -+ config_opts.shaper_config.oal = oal; -+ config_opts.shaper_config.crtcr = cpu_to_be24((lni->cr_token_rate.whole -+ << 13) | lni->cr_token_rate.fraction); -+ config_opts.shaper_config.ertcr = cpu_to_be24((lni->er_token_rate.whole -+ << 13) | lni->er_token_rate.fraction); -+ config_opts.shaper_config.crtbl = -+ cpu_to_be16(lni->cr_token_bucket_limit); -+ config_opts.shaper_config.ertbl = -+ cpu_to_be16(lni->er_token_bucket_limit); -+ config_opts.shaper_config.mps = 60; -+ -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_enable_shaper); -+ -+int qman_ceetm_lni_disable_shaper(struct qm_ceetm_lni *lni) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ -+ if (!lni->shaper_enable) { -+ pr_err("The shaper has been disabled\n"); -+ return -EINVAL; -+ } -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ config_opts.dcpid = lni->dcp_idx; -+ config_opts.shaper_config.cpl = lni->shaper_couple; -+ config_opts.shaper_config.oal = lni->oal; -+ config_opts.shaper_config.crtbl = -+ cpu_to_be16(lni->cr_token_bucket_limit); -+ config_opts.shaper_config.ertbl = -+ cpu_to_be16(lni->er_token_bucket_limit); -+ /* Set CR/ER rate with all 1's to configure an infinite rate, thus -+ * disable the shaping. -+ */ -+ config_opts.shaper_config.crtcr = 0xFFFFFF; -+ config_opts.shaper_config.ertcr = 0xFFFFFF; -+ config_opts.shaper_config.mps = 60; -+ lni->shaper_enable = 0; -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_disable_shaper); -+ -+int qman_ceetm_lni_is_shaper_enabled(struct qm_ceetm_lni *lni) -+{ -+ return lni->shaper_enable; -+} -+EXPORT_SYMBOL(qman_ceetm_lni_is_shaper_enabled); -+ -+int qman_ceetm_lni_set_commit_rate(struct qm_ceetm_lni *lni, -+ const struct qm_ceetm_rate *token_rate, -+ u16 token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ lni->cr_token_rate.whole = token_rate->whole; -+ lni->cr_token_rate.fraction = token_rate->fraction; -+ lni->cr_token_bucket_limit = token_limit; -+ if (!lni->shaper_enable) -+ return 0; -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ query_opts.dcpid = lni->dcp_idx; -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, -+ &query_result); -+ if (ret) { -+ pr_err("Fail to get current LNI shaper setting\n"); -+ return -EINVAL; -+ } -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ config_opts.dcpid = lni->dcp_idx; -+ config_opts.shaper_config.crtcr = cpu_to_be24((token_rate->whole << 13) -+ | (token_rate->fraction)); -+ config_opts.shaper_config.crtbl = cpu_to_be16(token_limit); -+ config_opts.shaper_config.cpl = query_result.shaper_query.cpl; -+ config_opts.shaper_config.oal = query_result.shaper_query.oal; -+ config_opts.shaper_config.ertcr = query_result.shaper_query.ertcr; -+ config_opts.shaper_config.ertbl = query_result.shaper_query.ertbl; -+ config_opts.shaper_config.mps = query_result.shaper_query.mps; -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_set_commit_rate); -+ -+int qman_ceetm_lni_set_commit_rate_bps(struct qm_ceetm_lni *lni, -+ u64 bps, -+ u16 token_limit) -+{ -+ struct qm_ceetm_rate token_rate; -+ int ret; -+ -+ ret = qman_ceetm_bps2tokenrate(bps, &token_rate, 0); -+ if (ret) { -+ pr_err("Can not convert bps to token rate\n"); -+ return -EINVAL; -+ } -+ -+ return qman_ceetm_lni_set_commit_rate(lni, &token_rate, token_limit); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_set_commit_rate_bps); -+ -+int qman_ceetm_lni_get_commit_rate(struct qm_ceetm_lni *lni, -+ struct qm_ceetm_rate *token_rate, -+ u16 *token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ query_opts.dcpid = lni->dcp_idx; -+ -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result); -+ if (ret) { -+ pr_err("The LNI CR rate or limit is not set\n"); -+ return -EINVAL; -+ } -+ token_rate->whole = be24_to_cpu(query_result.shaper_query.crtcr) >> 13; -+ token_rate->fraction = be24_to_cpu(query_result.shaper_query.crtcr) & -+ 0x1FFF; -+ *token_limit = be16_to_cpu(query_result.shaper_query.crtbl); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_lni_get_commit_rate); -+ -+int qman_ceetm_lni_get_commit_rate_bps(struct qm_ceetm_lni *lni, -+ u64 *bps, u16 *token_limit) -+{ -+ struct qm_ceetm_rate token_rate; -+ int ret; -+ -+ ret = qman_ceetm_lni_get_commit_rate(lni, &token_rate, token_limit); -+ if (ret) { -+ pr_err("The LNI CR rate or limit is not available\n"); -+ return -EINVAL; -+ } -+ -+ return qman_ceetm_tokenrate2bps(&token_rate, bps, 0); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_get_commit_rate_bps); -+ -+int qman_ceetm_lni_set_excess_rate(struct qm_ceetm_lni *lni, -+ const struct qm_ceetm_rate *token_rate, -+ u16 token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ lni->er_token_rate.whole = token_rate->whole; -+ lni->er_token_rate.fraction = token_rate->fraction; -+ lni->er_token_bucket_limit = token_limit; -+ if (!lni->shaper_enable) -+ return 0; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ query_opts.dcpid = lni->dcp_idx; -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, -+ &query_result); -+ if (ret) { -+ pr_err("Fail to get current LNI shaper setting\n"); -+ return -EINVAL; -+ } -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ config_opts.dcpid = lni->dcp_idx; -+ config_opts.shaper_config.ertcr = cpu_to_be24( -+ (token_rate->whole << 13) | (token_rate->fraction)); -+ config_opts.shaper_config.ertbl = cpu_to_be16(token_limit); -+ config_opts.shaper_config.cpl = query_result.shaper_query.cpl; -+ config_opts.shaper_config.oal = query_result.shaper_query.oal; -+ config_opts.shaper_config.crtcr = query_result.shaper_query.crtcr; -+ config_opts.shaper_config.crtbl = query_result.shaper_query.crtbl; -+ config_opts.shaper_config.mps = query_result.shaper_query.mps; -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_set_excess_rate); -+ -+int qman_ceetm_lni_set_excess_rate_bps(struct qm_ceetm_lni *lni, -+ u64 bps, -+ u16 token_limit) -+{ -+ struct qm_ceetm_rate token_rate; -+ int ret; -+ -+ ret = qman_ceetm_bps2tokenrate(bps, &token_rate, 0); -+ if (ret) { -+ pr_err("Can not convert bps to token rate\n"); -+ return -EINVAL; -+ } -+ return qman_ceetm_lni_set_excess_rate(lni, &token_rate, token_limit); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_set_excess_rate_bps); -+ -+int qman_ceetm_lni_get_excess_rate(struct qm_ceetm_lni *lni, -+ struct qm_ceetm_rate *token_rate, -+ u16 *token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx); -+ query_opts.dcpid = lni->dcp_idx; -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result); -+ if (ret) { -+ pr_err("The LNI ER rate or limit is not set\n"); -+ return -EINVAL; -+ } -+ token_rate->whole = be24_to_cpu(query_result.shaper_query.ertcr) >> 13; -+ token_rate->fraction = be24_to_cpu(query_result.shaper_query.ertcr) & -+ 0x1FFF; -+ *token_limit = be16_to_cpu(query_result.shaper_query.ertbl); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_lni_get_excess_rate); -+ -+int qman_ceetm_lni_get_excess_rate_bps(struct qm_ceetm_lni *lni, -+ u64 *bps, u16 *token_limit) -+{ -+ struct qm_ceetm_rate token_rate; -+ int ret; -+ -+ ret = qman_ceetm_lni_get_excess_rate(lni, &token_rate, token_limit); -+ if (ret) { -+ pr_err("The LNI ER rate or limit is not available\n"); -+ return -EINVAL; -+ } -+ -+ return qman_ceetm_tokenrate2bps(&token_rate, bps, 0); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_get_excess_rate_bps); -+ -+#define QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(n) ((15 - n) * 4) -+#define QMAN_CEETM_LNITCFCC_ENABLE 0x8 -+int qman_ceetm_lni_set_tcfcc(struct qm_ceetm_lni *lni, -+ unsigned int cq_level, -+ int traffic_class) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ u64 lnitcfcc; -+ -+ if ((cq_level > 15) | (traffic_class > 7)) { -+ pr_err("The CQ or traffic class id is out of range\n"); -+ return -EINVAL; -+ } -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_TCFC | lni->idx); -+ query_opts.dcpid = lni->dcp_idx; -+ if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) { -+ pr_err("Fail to query tcfcc\n"); -+ return -EINVAL; -+ } -+ -+ lnitcfcc = be64_to_cpu(query_result.tcfc_query.lnitcfcc); -+ if (traffic_class == -1) { -+ /* disable tcfc for this CQ */ -+ lnitcfcc &= ~((u64)QMAN_CEETM_LNITCFCC_ENABLE << -+ QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(cq_level)); -+ } else { -+ lnitcfcc &= ~((u64)0xF << -+ QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(cq_level)); -+ lnitcfcc |= ((u64)(QMAN_CEETM_LNITCFCC_ENABLE | -+ traffic_class)) << -+ QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(cq_level); -+ } -+ config_opts.tcfc_config.lnitcfcc = cpu_to_be64(lnitcfcc); -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_TCFC | lni->idx); -+ config_opts.dcpid = lni->dcp_idx; -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_lni_set_tcfcc); -+ -+#define QMAN_CEETM_LNITCFCC_TC_MASK 0x7 -+int qman_ceetm_lni_get_tcfcc(struct qm_ceetm_lni *lni, unsigned int cq_level, -+ int *traffic_class) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ u8 lnitcfcc; -+ -+ if (cq_level > 15) { -+ pr_err("the CQ level is out of range\n"); -+ return -EINVAL; -+ } -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_TCFC | lni->idx); -+ query_opts.dcpid = lni->dcp_idx; -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result); -+ if (ret) -+ return ret; -+ lnitcfcc = (u8)be64_to_cpu((query_result.tcfc_query.lnitcfcc) >> -+ QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(cq_level)); -+ if (lnitcfcc & QMAN_CEETM_LNITCFCC_ENABLE) -+ *traffic_class = lnitcfcc & QMAN_CEETM_LNITCFCC_TC_MASK; -+ else -+ *traffic_class = -1; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_lni_get_tcfcc); -+ -+int qman_ceetm_channel_claim(struct qm_ceetm_channel **channel, -+ struct qm_ceetm_lni *lni) -+{ -+ struct qm_ceetm_channel *p; -+ u32 channel_idx; -+ int ret = 0; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ -+ if (lni->dcp_idx == qm_dc_portal_fman0) { -+ ret = qman_alloc_ceetm0_channel(&channel_idx); -+ } else if (lni->dcp_idx == qm_dc_portal_fman1) { -+ ret = qman_alloc_ceetm1_channel(&channel_idx); -+ } else { -+ pr_err("dcp_idx %u does not correspond to a known fman in this driver\n", -+ lni->dcp_idx); -+ return -EINVAL; -+ } -+ -+ if (ret) { -+ pr_err("The is no channel available for LNI#%d\n", lni->idx); -+ return -ENODEV; -+ } -+ -+ p = kzalloc(sizeof(*p), GFP_KERNEL); -+ if (!p) -+ return -ENOMEM; -+ p->idx = channel_idx; -+ p->dcp_idx = lni->dcp_idx; -+ p->lni_idx = lni->idx; -+ list_add_tail(&p->node, &lni->channels); -+ INIT_LIST_HEAD(&p->class_queues); -+ INIT_LIST_HEAD(&p->ccgs); -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING | -+ channel_idx); -+ config_opts.dcpid = lni->dcp_idx; -+ config_opts.channel_mapping.map_lni_id = lni->idx; -+ config_opts.channel_mapping.map_shaped = 0; -+ if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) { -+ pr_err("Can't map channel#%d for LNI#%d\n", -+ channel_idx, lni->idx); -+ return -EINVAL; -+ } -+ *channel = p; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_claim); -+ -+int qman_ceetm_channel_release(struct qm_ceetm_channel *channel) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ if (!list_empty(&channel->class_queues)) { -+ pr_err("CEETM channel#%d has class queue unreleased!\n", -+ channel->idx); -+ return -EBUSY; -+ } -+ if (!list_empty(&channel->ccgs)) { -+ pr_err("CEETM channel#%d has ccg unreleased!\n", -+ channel->idx); -+ return -EBUSY; -+ } -+ -+ /* channel->dcp_idx corresponds to known fman validation */ -+ if ((channel->dcp_idx != qm_dc_portal_fman0) && -+ (channel->dcp_idx != qm_dc_portal_fman1)) { -+ pr_err("dcp_idx %u does not correspond to a known fman in this driver\n", -+ channel->dcp_idx); -+ return -EINVAL; -+ } -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ config_opts.dcpid = channel->dcp_idx; -+ memset(&config_opts.shaper_config, 0, -+ sizeof(config_opts.shaper_config)); -+ if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) { -+ pr_err("Can't reset channel shapping parameters\n"); -+ return -EINVAL; -+ } -+ -+ if (channel->dcp_idx == qm_dc_portal_fman0) { -+ qman_release_ceetm0_channelid(channel->idx); -+ } else if (channel->dcp_idx == qm_dc_portal_fman1) { -+ qman_release_ceetm1_channelid(channel->idx); -+ } else { -+ pr_err("dcp_idx %u does not correspond to a known fman in this driver\n", -+ channel->dcp_idx); -+ return -EINVAL; -+ } -+ list_del(&channel->node); -+ kfree(channel); -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_release); -+ -+int qman_ceetm_channel_enable_shaper(struct qm_ceetm_channel *channel, -+ int coupled) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ -+ if (channel->shaper_enable == 1) { -+ pr_err("This channel shaper has been enabled!\n"); -+ return -EINVAL; -+ } -+ -+ channel->shaper_enable = 1; -+ channel->shaper_couple = coupled; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING | -+ channel->idx); -+ query_opts.dcpid = channel->dcp_idx; -+ -+ if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) { -+ pr_err("Can't query channel mapping\n"); -+ return -EINVAL; -+ } -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING | -+ channel->idx); -+ config_opts.dcpid = channel->dcp_idx; -+ config_opts.channel_mapping.map_lni_id = -+ query_result.channel_mapping_query.map_lni_id; -+ config_opts.channel_mapping.map_shaped = 1; -+ if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) { -+ pr_err("Can't enable shaper for channel #%d\n", channel->idx); -+ return -EINVAL; -+ } -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ config_opts.shaper_config.cpl = coupled; -+ config_opts.shaper_config.crtcr = -+ cpu_to_be24((channel->cr_token_rate.whole -+ << 13) | -+ channel->cr_token_rate.fraction); -+ config_opts.shaper_config.ertcr = -+ cpu_to_be24(channel->er_token_rate.whole -+ << 13 | -+ channel->er_token_rate.fraction); -+ config_opts.shaper_config.crtbl = -+ cpu_to_be16(channel->cr_token_bucket_limit); -+ config_opts.shaper_config.ertbl = -+ cpu_to_be16(channel->er_token_bucket_limit); -+ -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_enable_shaper); -+ -+int qman_ceetm_channel_disable_shaper(struct qm_ceetm_channel *channel) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING | -+ channel->idx); -+ query_opts.dcpid = channel->dcp_idx; -+ -+ if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) { -+ pr_err("Can't query channel mapping\n"); -+ return -EINVAL; -+ } -+ -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING | -+ channel->idx); -+ config_opts.dcpid = channel->dcp_idx; -+ config_opts.channel_mapping.map_shaped = 0; -+ config_opts.channel_mapping.map_lni_id = -+ query_result.channel_mapping_query.map_lni_id; -+ -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_disable_shaper); -+ -+int qman_ceetm_channel_is_shaper_enabled(struct qm_ceetm_channel *channel) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING | -+ channel->idx); -+ query_opts.dcpid = channel->dcp_idx; -+ -+ if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) { -+ pr_err("Can't query channel mapping\n"); -+ return -EINVAL; -+ } -+ -+ return query_result.channel_mapping_query.map_shaped; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_is_shaper_enabled); -+ -+int qman_ceetm_channel_set_commit_rate(struct qm_ceetm_channel *channel, -+ const struct qm_ceetm_rate *token_rate, -+ u16 token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ query_opts.dcpid = channel->dcp_idx; -+ -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result); -+ if (ret) { -+ pr_err("Fail to get the current channel shaper setting\n"); -+ return -EINVAL; -+ } -+ -+ channel->cr_token_rate.whole = token_rate->whole; -+ channel->cr_token_rate.fraction = token_rate->fraction; -+ channel->cr_token_bucket_limit = token_limit; -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ config_opts.dcpid = channel->dcp_idx; -+ config_opts.shaper_config.crtcr = cpu_to_be24((token_rate->whole -+ << 13) | (token_rate->fraction)); -+ config_opts.shaper_config.crtbl = cpu_to_be16(token_limit); -+ config_opts.shaper_config.cpl = query_result.shaper_query.cpl; -+ config_opts.shaper_config.ertcr = query_result.shaper_query.ertcr; -+ config_opts.shaper_config.ertbl = query_result.shaper_query.ertbl; -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_commit_rate); -+ -+int qman_ceetm_channel_set_commit_rate_bps(struct qm_ceetm_channel *channel, -+ u64 bps, u16 token_limit) -+{ -+ struct qm_ceetm_rate token_rate; -+ int ret; -+ -+ ret = qman_ceetm_bps2tokenrate(bps, &token_rate, 0); -+ if (ret) { -+ pr_err("Can not convert bps to token rate\n"); -+ return -EINVAL; -+ } -+ return qman_ceetm_channel_set_commit_rate(channel, &token_rate, -+ token_limit); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_commit_rate_bps); -+ -+int qman_ceetm_channel_get_commit_rate(struct qm_ceetm_channel *channel, -+ struct qm_ceetm_rate *token_rate, -+ u16 *token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ query_opts.dcpid = channel->dcp_idx; -+ -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result); -+ if (ret | !query_result.shaper_query.crtcr | -+ !query_result.shaper_query.crtbl) { -+ pr_err("The channel commit rate or limit is not set\n"); -+ return -EINVAL; -+ } -+ token_rate->whole = be24_to_cpu(query_result.shaper_query.crtcr) >> 13; -+ token_rate->fraction = be24_to_cpu(query_result.shaper_query.crtcr) & -+ 0x1FFF; -+ *token_limit = be16_to_cpu(query_result.shaper_query.crtbl); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_get_commit_rate); -+ -+int qman_ceetm_channel_get_commit_rate_bps(struct qm_ceetm_channel *channel, -+ u64 *bps, u16 *token_limit) -+{ -+ struct qm_ceetm_rate token_rate; -+ int ret; -+ -+ ret = qman_ceetm_channel_get_commit_rate(channel, &token_rate, -+ token_limit); -+ if (ret) { -+ pr_err("The channel CR rate or limit is not available\n"); -+ return -EINVAL; -+ } -+ -+ return qman_ceetm_tokenrate2bps(&token_rate, bps, 0); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_get_commit_rate_bps); -+ -+int qman_ceetm_channel_set_excess_rate(struct qm_ceetm_channel *channel, -+ const struct qm_ceetm_rate *token_rate, -+ u16 token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ query_opts.dcpid = channel->dcp_idx; -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result); -+ if (ret) { -+ pr_err("Fail to get the current channel shaper setting\n"); -+ return -EINVAL; -+ } -+ -+ channel->er_token_rate.whole = token_rate->whole; -+ channel->er_token_rate.fraction = token_rate->fraction; -+ channel->er_token_bucket_limit = token_limit; -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ config_opts.dcpid = channel->dcp_idx; -+ config_opts.shaper_config.ertcr = cpu_to_be24( -+ (token_rate->whole << 13) | (token_rate->fraction)); -+ config_opts.shaper_config.ertbl = cpu_to_be16(token_limit); -+ config_opts.shaper_config.cpl = query_result.shaper_query.cpl; -+ config_opts.shaper_config.crtcr = query_result.shaper_query.crtcr; -+ config_opts.shaper_config.crtbl = query_result.shaper_query.crtbl; -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_excess_rate); -+ -+int qman_ceetm_channel_set_excess_rate_bps(struct qm_ceetm_channel *channel, -+ u64 bps, u16 token_limit) -+{ -+ struct qm_ceetm_rate token_rate; -+ int ret; -+ -+ ret = qman_ceetm_bps2tokenrate(bps, &token_rate, 0); -+ if (ret) { -+ pr_err("Can not convert bps to token rate\n"); -+ return -EINVAL; -+ } -+ return qman_ceetm_channel_set_excess_rate(channel, &token_rate, -+ token_limit); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_excess_rate_bps); -+ -+int qman_ceetm_channel_get_excess_rate(struct qm_ceetm_channel *channel, -+ struct qm_ceetm_rate *token_rate, -+ u16 *token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ query_opts.dcpid = channel->dcp_idx; -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result); -+ if (ret | !query_result.shaper_query.ertcr | -+ !query_result.shaper_query.ertbl) { -+ pr_err("The channel excess rate or limit is not set\n"); -+ return -EINVAL; -+ } -+ token_rate->whole = be24_to_cpu(query_result.shaper_query.ertcr) >> 13; -+ token_rate->fraction = be24_to_cpu(query_result.shaper_query.ertcr) & -+ 0x1FFF; -+ *token_limit = be16_to_cpu(query_result.shaper_query.ertbl); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_get_excess_rate); -+ -+int qman_ceetm_channel_get_excess_rate_bps(struct qm_ceetm_channel *channel, -+ u64 *bps, u16 *token_limit) -+{ -+ struct qm_ceetm_rate token_rate; -+ int ret; -+ -+ ret = qman_ceetm_channel_get_excess_rate(channel, &token_rate, -+ token_limit); -+ if (ret) { -+ pr_err("The channel ER rate or limit is not available\n"); -+ return -EINVAL; -+ } -+ -+ return qman_ceetm_tokenrate2bps(&token_rate, bps, 0); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_get_excess_rate_bps); -+ -+int qman_ceetm_channel_set_weight(struct qm_ceetm_channel *channel, -+ u16 token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts; -+ -+ if (channel->shaper_enable) { -+ pr_err("This channel is a shaped one\n"); -+ return -EINVAL; -+ } -+ -+ channel->cr_token_bucket_limit = token_limit; -+ config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ config_opts.dcpid = channel->dcp_idx; -+ config_opts.shaper_config.crtbl = cpu_to_be16(token_limit); -+ return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_weight); -+ -+int qman_ceetm_channel_get_weight(struct qm_ceetm_channel *channel, -+ u16 *token_limit) -+{ -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result; -+ int ret; -+ -+ query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER | -+ channel->idx); -+ query_opts.dcpid = channel->dcp_idx; -+ ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result); -+ if (ret | !query_result.shaper_query.crtbl) { -+ pr_err("This unshaped channel's uFQ wight is unavailable\n"); -+ return -EINVAL; -+ } -+ *token_limit = be16_to_cpu(query_result.shaper_query.crtbl); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_get_weight); -+ -+int qman_ceetm_channel_set_group(struct qm_ceetm_channel *channel, int group_b, -+ unsigned int prio_a, unsigned int prio_b) -+{ -+ struct qm_mcc_ceetm_class_scheduler_config config_opts; -+ struct qm_mcr_ceetm_class_scheduler_query query_result; -+ int i; -+ -+ if (prio_a > 7) { -+ pr_err("The priority of group A is out of range\n"); -+ return -EINVAL; -+ } -+ if (group_b && (prio_b > 7)) { -+ pr_err("The priority of group B is out of range\n"); -+ return -EINVAL; -+ } -+ -+ if (qman_ceetm_query_class_scheduler(channel, &query_result)) { -+ pr_err("Can't query channel#%d's scheduler!\n", channel->idx); -+ return -EINVAL; -+ } -+ -+ config_opts.cqcid = cpu_to_be16(channel->idx); -+ config_opts.dcpid = channel->dcp_idx; -+ config_opts.gpc_combine_flag = !group_b; -+ config_opts.gpc_prio_a = prio_a; -+ config_opts.gpc_prio_b = prio_b; -+ -+ for (i = 0; i < 8; i++) -+ config_opts.w[i] = query_result.w[i]; -+ config_opts.crem = query_result.crem; -+ config_opts.erem = query_result.erem; -+ -+ return qman_ceetm_configure_class_scheduler(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_group); -+ -+int qman_ceetm_channel_get_group(struct qm_ceetm_channel *channel, int *group_b, -+ unsigned int *prio_a, unsigned int *prio_b) -+{ -+ struct qm_mcr_ceetm_class_scheduler_query query_result; -+ -+ if (qman_ceetm_query_class_scheduler(channel, &query_result)) { -+ pr_err("Can't query channel#%d's scheduler!\n", channel->idx); -+ return -EINVAL; -+ } -+ *group_b = !query_result.gpc_combine_flag; -+ *prio_a = query_result.gpc_prio_a; -+ *prio_b = query_result.gpc_prio_b; -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_get_group); -+ -+#define GROUP_A_ELIGIBILITY_SET (1 << 8) -+#define GROUP_B_ELIGIBILITY_SET (1 << 9) -+#define CQ_ELIGIBILITY_SET(n) (1 << (7 - n)) -+int qman_ceetm_channel_set_group_cr_eligibility(struct qm_ceetm_channel -+ *channel, int group_b, int cre) -+{ -+ struct qm_mcc_ceetm_class_scheduler_config csch_config; -+ struct qm_mcr_ceetm_class_scheduler_query csch_query; -+ int i; -+ -+ if (qman_ceetm_query_class_scheduler(channel, &csch_query)) { -+ pr_err("Cannot get the channel %d scheduler setting.\n", -+ channel->idx); -+ return -EINVAL; -+ } -+ csch_config.cqcid = cpu_to_be16(channel->idx); -+ csch_config.dcpid = channel->dcp_idx; -+ csch_config.gpc_combine_flag = csch_query.gpc_combine_flag; -+ csch_config.gpc_prio_a = csch_query.gpc_prio_a; -+ csch_config.gpc_prio_b = csch_query.gpc_prio_b; -+ -+ for (i = 0; i < 8; i++) -+ csch_config.w[i] = csch_query.w[i]; -+ csch_config.erem = csch_query.erem; -+ if (group_b) -+ csch_config.crem = (be16_to_cpu(csch_query.crem) -+ & ~GROUP_B_ELIGIBILITY_SET) -+ | (cre ? GROUP_B_ELIGIBILITY_SET : 0); -+ else -+ csch_config.crem = (be16_to_cpu(csch_query.crem) -+ & ~GROUP_A_ELIGIBILITY_SET) -+ | (cre ? GROUP_A_ELIGIBILITY_SET : 0); -+ -+ csch_config.crem = cpu_to_be16(csch_config.crem); -+ -+ if (qman_ceetm_configure_class_scheduler(&csch_config)) { -+ pr_err("Cannot config channel %d's scheduler with " -+ "group_%c's cr eligibility\n", channel->idx, -+ group_b ? 'b' : 'a'); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_group_cr_eligibility); -+ -+int qman_ceetm_channel_set_group_er_eligibility(struct qm_ceetm_channel -+ *channel, int group_b, int ere) -+{ -+ struct qm_mcc_ceetm_class_scheduler_config csch_config; -+ struct qm_mcr_ceetm_class_scheduler_query csch_query; -+ int i; -+ -+ if (qman_ceetm_query_class_scheduler(channel, &csch_query)) { -+ pr_err("Cannot get the channel %d scheduler setting.\n", -+ channel->idx); -+ return -EINVAL; -+ } -+ csch_config.cqcid = cpu_to_be16(channel->idx); -+ csch_config.dcpid = channel->dcp_idx; -+ csch_config.gpc_combine_flag = csch_query.gpc_combine_flag; -+ csch_config.gpc_prio_a = csch_query.gpc_prio_a; -+ csch_config.gpc_prio_b = csch_query.gpc_prio_b; -+ -+ for (i = 0; i < 8; i++) -+ csch_config.w[i] = csch_query.w[i]; -+ csch_config.crem = csch_query.crem; -+ if (group_b) -+ csch_config.erem = (be16_to_cpu(csch_query.erem) -+ & ~GROUP_B_ELIGIBILITY_SET) -+ | (ere ? GROUP_B_ELIGIBILITY_SET : 0); -+ else -+ csch_config.erem = (be16_to_cpu(csch_query.erem) -+ & ~GROUP_A_ELIGIBILITY_SET) -+ | (ere ? GROUP_A_ELIGIBILITY_SET : 0); -+ -+ csch_config.erem = cpu_to_be16(csch_config.erem); -+ -+ if (qman_ceetm_configure_class_scheduler(&csch_config)) { -+ pr_err("Cannot config channel %d's scheduler with " -+ "group_%c's er eligibility\n", channel->idx, -+ group_b ? 'b' : 'a'); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_group_er_eligibility); -+ -+int qman_ceetm_channel_set_cq_cr_eligibility(struct qm_ceetm_channel *channel, -+ unsigned int idx, int cre) -+{ -+ struct qm_mcc_ceetm_class_scheduler_config csch_config; -+ struct qm_mcr_ceetm_class_scheduler_query csch_query; -+ int i; -+ -+ if (idx > 7) { -+ pr_err("CQ index is out of range\n"); -+ return -EINVAL; -+ } -+ if (qman_ceetm_query_class_scheduler(channel, &csch_query)) { -+ pr_err("Cannot get the channel %d scheduler setting.\n", -+ channel->idx); -+ return -EINVAL; -+ } -+ csch_config.cqcid = cpu_to_be16(channel->idx); -+ csch_config.dcpid = channel->dcp_idx; -+ csch_config.gpc_combine_flag = csch_query.gpc_combine_flag; -+ csch_config.gpc_prio_a = csch_query.gpc_prio_a; -+ csch_config.gpc_prio_b = csch_query.gpc_prio_b; -+ for (i = 0; i < 8; i++) -+ csch_config.w[i] = csch_query.w[i]; -+ csch_config.erem = csch_query.erem; -+ csch_config.crem = (be16_to_cpu(csch_query.crem) -+ & ~CQ_ELIGIBILITY_SET(idx)) | -+ (cre ? CQ_ELIGIBILITY_SET(idx) : 0); -+ csch_config.crem = cpu_to_be16(csch_config.crem); -+ if (qman_ceetm_configure_class_scheduler(&csch_config)) { -+ pr_err("Cannot config channel scheduler to set " -+ "cr eligibility mask for CQ#%d\n", idx); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_cq_cr_eligibility); -+ -+int qman_ceetm_channel_set_cq_er_eligibility(struct qm_ceetm_channel *channel, -+ unsigned int idx, int ere) -+{ -+ struct qm_mcc_ceetm_class_scheduler_config csch_config; -+ struct qm_mcr_ceetm_class_scheduler_query csch_query; -+ int i; -+ -+ if (idx > 7) { -+ pr_err("CQ index is out of range\n"); -+ return -EINVAL; -+ } -+ if (qman_ceetm_query_class_scheduler(channel, &csch_query)) { -+ pr_err("Cannot get the channel %d scheduler setting.\n", -+ channel->idx); -+ return -EINVAL; -+ } -+ csch_config.cqcid = cpu_to_be16(channel->idx); -+ csch_config.dcpid = channel->dcp_idx; -+ csch_config.gpc_combine_flag = csch_query.gpc_combine_flag; -+ csch_config.gpc_prio_a = csch_query.gpc_prio_a; -+ csch_config.gpc_prio_b = csch_query.gpc_prio_b; -+ for (i = 0; i < 8; i++) -+ csch_config.w[i] = csch_query.w[i]; -+ csch_config.crem = csch_query.crem; -+ csch_config.erem = (be16_to_cpu(csch_query.erem) -+ & ~CQ_ELIGIBILITY_SET(idx)) | -+ (ere ? CQ_ELIGIBILITY_SET(idx) : 0); -+ csch_config.erem = cpu_to_be16(csch_config.erem); -+ if (qman_ceetm_configure_class_scheduler(&csch_config)) { -+ pr_err("Cannot config channel scheduler to set " -+ "er eligibility mask for CQ#%d\n", idx); -+ return -EINVAL; -+ } -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_channel_set_cq_er_eligibility); -+ -+int qman_ceetm_cq_claim(struct qm_ceetm_cq **cq, -+ struct qm_ceetm_channel *channel, unsigned int idx, -+ struct qm_ceetm_ccg *ccg) -+{ -+ struct qm_ceetm_cq *p; -+ struct qm_mcc_ceetm_cq_config cq_config; -+ -+ if (idx > 7) { -+ pr_err("The independent class queue id is out of range\n"); -+ return -EINVAL; -+ } -+ -+ list_for_each_entry(p, &channel->class_queues, node) { -+ if (p->idx == idx) { -+ pr_err("The CQ#%d has been claimed!\n", idx); -+ return -EINVAL; -+ } -+ } -+ -+ p = kmalloc(sizeof(*p), GFP_KERNEL); -+ if (!p) { -+ pr_err("Can't allocate memory for CQ#%d!\n", idx); -+ return -ENOMEM; -+ } -+ -+ list_add_tail(&p->node, &channel->class_queues); -+ p->idx = idx; -+ p->is_claimed = 1; -+ p->parent = channel; -+ INIT_LIST_HEAD(&p->bound_lfqids); -+ -+ if (ccg) { -+ cq_config.cqid = cpu_to_be16((channel->idx << 4) | idx); -+ cq_config.dcpid = channel->dcp_idx; -+ cq_config.ccgid = cpu_to_be16(ccg->idx); -+ if (qman_ceetm_configure_cq(&cq_config)) { -+ pr_err("Can't configure the CQ#%d with CCGRID#%d\n", -+ idx, ccg->idx); -+ list_del(&p->node); -+ kfree(p); -+ return -EINVAL; -+ } -+ } -+ -+ *cq = p; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_cq_claim); -+ -+int qman_ceetm_cq_claim_A(struct qm_ceetm_cq **cq, -+ struct qm_ceetm_channel *channel, unsigned int idx, -+ struct qm_ceetm_ccg *ccg) -+{ -+ struct qm_ceetm_cq *p; -+ struct qm_mcc_ceetm_cq_config cq_config; -+ -+ if ((idx < 8) || (idx > 15)) { -+ pr_err("This grouped class queue id is out of range\n"); -+ return -EINVAL; -+ } -+ -+ list_for_each_entry(p, &channel->class_queues, node) { -+ if (p->idx == idx) { -+ pr_err("The CQ#%d has been claimed!\n", idx); -+ return -EINVAL; -+ } -+ } -+ -+ p = kmalloc(sizeof(*p), GFP_KERNEL); -+ if (!p) { -+ pr_err("Can't allocate memory for CQ#%d!\n", idx); -+ return -ENOMEM; -+ } -+ -+ list_add_tail(&p->node, &channel->class_queues); -+ p->idx = idx; -+ p->is_claimed = 1; -+ p->parent = channel; -+ INIT_LIST_HEAD(&p->bound_lfqids); -+ -+ if (ccg) { -+ cq_config.cqid = cpu_to_be16((channel->idx << 4) | idx); -+ cq_config.dcpid = channel->dcp_idx; -+ cq_config.ccgid = cpu_to_be16(ccg->idx); -+ if (qman_ceetm_configure_cq(&cq_config)) { -+ pr_err("Can't configure the CQ#%d with CCGRID#%d\n", -+ idx, ccg->idx); -+ list_del(&p->node); -+ kfree(p); -+ return -EINVAL; -+ } -+ } -+ *cq = p; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_cq_claim_A); -+ -+int qman_ceetm_cq_claim_B(struct qm_ceetm_cq **cq, -+ struct qm_ceetm_channel *channel, unsigned int idx, -+ struct qm_ceetm_ccg *ccg) -+{ -+ struct qm_ceetm_cq *p; -+ struct qm_mcc_ceetm_cq_config cq_config; -+ -+ if ((idx < 12) || (idx > 15)) { -+ pr_err("This grouped class queue id is out of range\n"); -+ return -EINVAL; -+ } -+ -+ list_for_each_entry(p, &channel->class_queues, node) { -+ if (p->idx == idx) { -+ pr_err("The CQ#%d has been claimed!\n", idx); -+ return -EINVAL; -+ } -+ } -+ -+ p = kmalloc(sizeof(*p), GFP_KERNEL); -+ if (!p) { -+ pr_err("Can't allocate memory for CQ#%d!\n", idx); -+ return -ENOMEM; -+ } -+ -+ list_add_tail(&p->node, &channel->class_queues); -+ p->idx = idx; -+ p->is_claimed = 1; -+ p->parent = channel; -+ INIT_LIST_HEAD(&p->bound_lfqids); -+ -+ if (ccg) { -+ cq_config.cqid = cpu_to_be16((channel->idx << 4) | idx); -+ cq_config.dcpid = channel->dcp_idx; -+ cq_config.ccgid = cpu_to_be16(ccg->idx); -+ if (qman_ceetm_configure_cq(&cq_config)) { -+ pr_err("Can't configure the CQ#%d with CCGRID#%d\n", -+ idx, ccg->idx); -+ list_del(&p->node); -+ kfree(p); -+ return -EINVAL; -+ } -+ } -+ *cq = p; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_cq_claim_B); -+ -+int qman_ceetm_cq_release(struct qm_ceetm_cq *cq) -+{ -+ if (!list_empty(&cq->bound_lfqids)) { -+ pr_err("The CQ#%d has unreleased LFQID\n", cq->idx); -+ return -EBUSY; -+ } -+ list_del(&cq->node); -+ qman_ceetm_drain_cq(cq); -+ kfree(cq); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_cq_release); -+ -+int qman_ceetm_set_queue_weight(struct qm_ceetm_cq *cq, -+ struct qm_ceetm_weight_code *weight_code) -+{ -+ struct qm_mcc_ceetm_class_scheduler_config config_opts; -+ struct qm_mcr_ceetm_class_scheduler_query query_result; -+ int i; -+ -+ if (cq->idx < 8) { -+ pr_err("Can not set weight for ungrouped class queue\n"); -+ return -EINVAL; -+ } -+ -+ if (qman_ceetm_query_class_scheduler(cq->parent, &query_result)) { -+ pr_err("Can't query channel#%d's scheduler!\n", -+ cq->parent->idx); -+ return -EINVAL; -+ } -+ -+ config_opts.cqcid = cpu_to_be16(cq->parent->idx); -+ config_opts.dcpid = cq->parent->dcp_idx; -+ config_opts.crem = query_result.crem; -+ config_opts.erem = query_result.erem; -+ config_opts.gpc_combine_flag = query_result.gpc_combine_flag; -+ config_opts.gpc_prio_a = query_result.gpc_prio_a; -+ config_opts.gpc_prio_b = query_result.gpc_prio_b; -+ -+ for (i = 0; i < 8; i++) -+ config_opts.w[i] = query_result.w[i]; -+ config_opts.w[cq->idx - 8] = ((weight_code->y << 3) | -+ (weight_code->x & 0x7)); -+ return qman_ceetm_configure_class_scheduler(&config_opts); -+} -+EXPORT_SYMBOL(qman_ceetm_set_queue_weight); -+ -+int qman_ceetm_get_queue_weight(struct qm_ceetm_cq *cq, -+ struct qm_ceetm_weight_code *weight_code) -+{ -+ struct qm_mcr_ceetm_class_scheduler_query query_result; -+ -+ if (cq->idx < 8) { -+ pr_err("Can not get weight for ungrouped class queue\n"); -+ return -EINVAL; -+ } -+ -+ if (qman_ceetm_query_class_scheduler(cq->parent, -+ &query_result)) { -+ pr_err("Can't get the weight code for CQ#%d!\n", cq->idx); -+ return -EINVAL; -+ } -+ weight_code->y = query_result.w[cq->idx - 8] >> 3; -+ weight_code->x = query_result.w[cq->idx - 8] & 0x7; -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_get_queue_weight); -+ -+/* The WBFS code is represent as {x,y}, the effect wieght can be calculated as: -+ * effective weight = 2^x / (1 - (y/64)) -+ * = 2^(x+6) / (64 - y) -+ */ -+static void reduce_fraction(u32 *n, u32 *d) -+{ -+ u32 factor = 2; -+ u32 lesser = (*n < *d) ? *n : *d; -+ /* If factor exceeds the square-root of the lesser of *n and *d, -+ * then there's no point continuing. Proof: if there was a factor -+ * bigger than the square root, that would imply there exists -+ * another factor smaller than the square-root with which it -+ * multiplies to give 'lesser' - but that's a contradiction -+ * because the other factor would have already been found and -+ * divided out. -+ */ -+ while ((factor * factor) <= lesser) { -+ /* If 'factor' is a factor of *n and *d, divide them both -+ * by 'factor' as many times as possible. -+ */ -+ while (!(*n % factor) && !(*d % factor)) { -+ *n /= factor; -+ *d /= factor; -+ lesser /= factor; -+ } -+ if (factor == 2) -+ factor = 3; -+ else -+ factor += 2; -+ } -+} -+ -+int qman_ceetm_wbfs2ratio(struct qm_ceetm_weight_code *weight_code, -+ u32 *numerator, -+ u32 *denominator) -+{ -+ *numerator = (u32) 1 << (weight_code->x + 6); -+ *denominator = 64 - weight_code->y; -+ reduce_fraction(numerator, denominator); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_wbfs2ratio); -+ -+/* For a given x, the weight is between 2^x (inclusive) and 2^(x+1) (exclusive). -+ * So find 'x' by range, and then estimate 'y' using: -+ * 64 - y = 2^(x + 6) / weight -+ * = 2^(x + 6) / (n/d) -+ * = d * 2^(x+6) / n -+ * y = 64 - (d * 2^(x+6) / n) -+ */ -+int qman_ceetm_ratio2wbfs(u32 numerator, -+ u32 denominator, -+ struct qm_ceetm_weight_code *weight_code, -+ int rounding) -+{ -+ unsigned int y, x = 0; -+ /* search incrementing 'x' until: -+ * weight < 2^(x+1) -+ * n/d < 2^(x+1) -+ * n < d * 2^(x+1) -+ */ -+ while ((x < 8) && (numerator >= (denominator << (x + 1)))) -+ x++; -+ if (x >= 8) -+ return -ERANGE; -+ /* because of the subtraction, use '-rounding' */ -+ y = 64 - ROUNDING(denominator << (x + 6), numerator, -rounding); -+ if (y >= 32) -+ return -ERANGE; -+ weight_code->x = x; -+ weight_code->y = y; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_ratio2wbfs); -+ -+int qman_ceetm_set_queue_weight_in_ratio(struct qm_ceetm_cq *cq, u32 ratio) -+{ -+ struct qm_ceetm_weight_code weight_code; -+ -+ if (qman_ceetm_ratio2wbfs(ratio, 100, &weight_code, 0)) { -+ pr_err("Cannot get wbfs code for cq %x\n", cq->idx); -+ return -EINVAL; -+ } -+ return qman_ceetm_set_queue_weight(cq, &weight_code); -+} -+EXPORT_SYMBOL(qman_ceetm_set_queue_weight_in_ratio); -+ -+int qman_ceetm_get_queue_weight_in_ratio(struct qm_ceetm_cq *cq, u32 *ratio) -+{ -+ struct qm_ceetm_weight_code weight_code; -+ u32 n, d; -+ -+ if (qman_ceetm_get_queue_weight(cq, &weight_code)) { -+ pr_err("Cannot query the weight code for cq%x\n", cq->idx); -+ return -EINVAL; -+ } -+ -+ if (qman_ceetm_wbfs2ratio(&weight_code, &n, &d)) { -+ pr_err("Cannot get the ratio with wbfs code\n"); -+ return -EINVAL; -+ } -+ -+ *ratio = (n * 100) / d; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_get_queue_weight_in_ratio); -+ -+int qman_ceetm_cq_get_dequeue_statistics(struct qm_ceetm_cq *cq, u32 flags, -+ u64 *frame_count, u64 *byte_count) -+{ -+ struct qm_mcr_ceetm_statistics_query result; -+ u16 cid, command_type; -+ enum qm_dc_portal dcp_idx; -+ int ret; -+ -+ cid = cpu_to_be16((cq->parent->idx << 4) | cq->idx); -+ dcp_idx = cq->parent->dcp_idx; -+ if (flags == QMAN_CEETM_FLAG_CLEAR_STATISTICS_COUNTER) -+ command_type = CEETM_QUERY_DEQUEUE_CLEAR_STATISTICS; -+ else -+ command_type = CEETM_QUERY_DEQUEUE_STATISTICS; -+ -+ ret = qman_ceetm_query_statistics(cid, dcp_idx, command_type, &result); -+ if (ret) { -+ pr_err("Can't query the statistics of CQ#%d!\n", cq->idx); -+ return -EINVAL; -+ } -+ -+ *frame_count = be40_to_cpu(result.frm_cnt); -+ *byte_count = be48_to_cpu(result.byte_cnt); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_cq_get_dequeue_statistics); -+ -+int qman_ceetm_drain_cq(struct qm_ceetm_cq *cq) -+{ -+ struct qm_mcr_ceetm_cq_peek_pop_xsfdrread ppxr; -+ int ret; -+ -+ do { -+ ret = qman_ceetm_cq_peek_pop_xsfdrread(cq, 1, 0, &ppxr); -+ if (ret) { -+ pr_err("Failed to pop frame from CQ\n"); -+ return -EINVAL; -+ } -+ } while (!(ppxr.stat & 0x2)); -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_drain_cq); -+ -+#define CEETM_LFQMT_LFQID_MSB 0xF00000 -+#define CEETM_LFQMT_LFQID_LSB 0x000FFF -+int qman_ceetm_lfq_claim(struct qm_ceetm_lfq **lfq, -+ struct qm_ceetm_cq *cq) -+{ -+ struct qm_ceetm_lfq *p; -+ u32 lfqid; -+ int ret = 0; -+ struct qm_mcc_ceetm_lfqmt_config lfqmt_config; -+ -+ if (cq->parent->dcp_idx == qm_dc_portal_fman0) { -+ ret = qman_alloc_ceetm0_lfqid(&lfqid); -+ } else if (cq->parent->dcp_idx == qm_dc_portal_fman1) { -+ ret = qman_alloc_ceetm1_lfqid(&lfqid); -+ } else { -+ pr_err("dcp_idx %u does not correspond to a known fman in this driver\n", -+ cq->parent->dcp_idx); -+ return -EINVAL; -+ } -+ -+ if (ret) { -+ pr_err("There is no lfqid avalaible for CQ#%d!\n", cq->idx); -+ return -ENODEV; -+ } -+ p = kmalloc(sizeof(*p), GFP_KERNEL); -+ if (!p) -+ return -ENOMEM; -+ p->idx = lfqid; -+ p->dctidx = (u16)(lfqid & CEETM_LFQMT_LFQID_LSB); -+ p->parent = cq->parent; -+ list_add_tail(&p->node, &cq->bound_lfqids); -+ -+ lfqmt_config.lfqid = cpu_to_be24(CEETM_LFQMT_LFQID_MSB | -+ (cq->parent->dcp_idx << 16) | -+ (lfqid & CEETM_LFQMT_LFQID_LSB)); -+ lfqmt_config.cqid = cpu_to_be16((cq->parent->idx << 4) | (cq->idx)); -+ lfqmt_config.dctidx = cpu_to_be16(p->dctidx); -+ if (qman_ceetm_configure_lfqmt(&lfqmt_config)) { -+ pr_err("Can't configure LFQMT for LFQID#%d @ CQ#%d\n", -+ lfqid, cq->idx); -+ list_del(&p->node); -+ kfree(p); -+ return -EINVAL; -+ } -+ *lfq = p; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_lfq_claim); -+ -+int qman_ceetm_lfq_release(struct qm_ceetm_lfq *lfq) -+{ -+ if (lfq->parent->dcp_idx == qm_dc_portal_fman0) { -+ qman_release_ceetm0_lfqid(lfq->idx); -+ } else if (lfq->parent->dcp_idx == qm_dc_portal_fman1) { -+ qman_release_ceetm1_lfqid(lfq->idx); -+ } else { -+ pr_err("dcp_idx %u does not correspond to a known fman in this driver\n", -+ lfq->parent->dcp_idx); -+ return -EINVAL; -+ } -+ list_del(&lfq->node); -+ kfree(lfq); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_lfq_release); -+ -+int qman_ceetm_lfq_set_context(struct qm_ceetm_lfq *lfq, u64 context_a, -+ u32 context_b) -+{ -+ struct qm_mcc_ceetm_dct_config dct_config; -+ lfq->context_a = context_a; -+ lfq->context_b = context_b; -+ dct_config.dctidx = cpu_to_be16((u16)lfq->dctidx); -+ dct_config.dcpid = lfq->parent->dcp_idx; -+ dct_config.context_b = cpu_to_be32(context_b); -+ dct_config.context_a = cpu_to_be64(context_a); -+ -+ return qman_ceetm_configure_dct(&dct_config); -+} -+EXPORT_SYMBOL(qman_ceetm_lfq_set_context); -+ -+int qman_ceetm_lfq_get_context(struct qm_ceetm_lfq *lfq, u64 *context_a, -+ u32 *context_b) -+{ -+ struct qm_mcc_ceetm_dct_query dct_query; -+ struct qm_mcr_ceetm_dct_query query_result; -+ -+ dct_query.dctidx = cpu_to_be16(lfq->dctidx); -+ dct_query.dcpid = lfq->parent->dcp_idx; -+ if (qman_ceetm_query_dct(&dct_query, &query_result)) { -+ pr_err("Can't query LFQID#%d's context!\n", lfq->idx); -+ return -EINVAL; -+ } -+ *context_a = be64_to_cpu(query_result.context_a); -+ *context_b = be32_to_cpu(query_result.context_b); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_lfq_get_context); -+ -+int qman_ceetm_create_fq(struct qm_ceetm_lfq *lfq, struct qman_fq *fq) -+{ -+ spin_lock_init(&fq->fqlock); -+ fq->fqid = lfq->idx; -+ fq->flags = QMAN_FQ_FLAG_NO_MODIFY; -+ if (lfq->ern) -+ fq->cb.ern = lfq->ern; -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ if (unlikely(find_empty_fq_table_entry(&fq->key, fq))) -+ return -ENOMEM; -+#endif -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_create_fq); -+ -+#define MAX_CCG_IDX 0x000F -+int qman_ceetm_ccg_claim(struct qm_ceetm_ccg **ccg, -+ struct qm_ceetm_channel *channel, -+ unsigned int idx, -+ void (*cscn)(struct qm_ceetm_ccg *, -+ void *cb_ctx, -+ int congested), -+ void *cb_ctx) -+{ -+ struct qm_ceetm_ccg *p; -+ -+ if (idx > MAX_CCG_IDX) { -+ pr_err("The given ccg index is out of range\n"); -+ return -EINVAL; -+ } -+ -+ list_for_each_entry(p, &channel->ccgs, node) { -+ if (p->idx == idx) { -+ pr_err("The CCG#%d has been claimed\n", idx); -+ return -EINVAL; -+ } -+ } -+ -+ p = kmalloc(sizeof(*p), GFP_KERNEL); -+ if (!p) { -+ pr_err("Can't allocate memory for CCG#%d!\n", idx); -+ return -ENOMEM; -+ } -+ -+ list_add_tail(&p->node, &channel->ccgs); -+ -+ p->idx = idx; -+ p->parent = channel; -+ p->cb = cscn; -+ p->cb_ctx = cb_ctx; -+ INIT_LIST_HEAD(&p->cb_node); -+ -+ *ccg = p; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_ccg_claim); -+ -+int qman_ceetm_ccg_release(struct qm_ceetm_ccg *ccg) -+{ -+ unsigned long irqflags __maybe_unused; -+ struct qm_mcc_ceetm_ccgr_config config_opts; -+ int ret = 0; -+ struct qman_portal *p = get_affine_portal(); -+ -+ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_ccgr_config)); -+ spin_lock_irqsave(&p->ccgr_lock, irqflags); -+ if (!list_empty(&ccg->cb_node)) -+ list_del(&ccg->cb_node); -+ config_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_CONFIGURE | -+ (ccg->parent->idx << 4) | ccg->idx); -+ config_opts.dcpid = ccg->parent->dcp_idx; -+ config_opts.we_mask = cpu_to_be16(QM_CCGR_WE_CSCN_TUPD); -+ config_opts.cm_config.cscn_tupd = cpu_to_be16(PORTAL_IDX(p)); -+ ret = qman_ceetm_configure_ccgr(&config_opts); -+ spin_unlock_irqrestore(&p->ccgr_lock, irqflags); -+ put_affine_portal(); -+ -+ list_del(&ccg->node); -+ kfree(ccg); -+ return ret; -+} -+EXPORT_SYMBOL(qman_ceetm_ccg_release); -+ -+int qman_ceetm_ccg_set(struct qm_ceetm_ccg *ccg, u16 we_mask, -+ const struct qm_ceetm_ccg_params *params) -+{ -+ struct qm_mcc_ceetm_ccgr_config config_opts; -+ unsigned long irqflags __maybe_unused; -+ int ret; -+ struct qman_portal *p; -+ -+ if (((ccg->parent->idx << 4) | ccg->idx) >= (2 * __CGR_NUM)) -+ return -EINVAL; -+ -+ p = get_affine_portal(); -+ -+ memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_ccgr_config)); -+ spin_lock_irqsave(&p->ccgr_lock, irqflags); -+ -+ config_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_CONFIGURE | -+ (ccg->parent->idx << 4) | ccg->idx); -+ config_opts.dcpid = ccg->parent->dcp_idx; -+ config_opts.we_mask = we_mask; -+ if (we_mask & QM_CCGR_WE_CSCN_EN) { -+ config_opts.we_mask |= QM_CCGR_WE_CSCN_TUPD; -+ config_opts.cm_config.cscn_tupd = cpu_to_be16( -+ QM_CGR_TARG_UDP_CTRL_WRITE_BIT | PORTAL_IDX(p)); -+ } -+ config_opts.we_mask = cpu_to_be16(config_opts.we_mask); -+ config_opts.cm_config.ctl_wr_en_g = params->wr_en_g; -+ config_opts.cm_config.ctl_wr_en_y = params->wr_en_y; -+ config_opts.cm_config.ctl_wr_en_r = params->wr_en_r; -+ config_opts.cm_config.ctl_td_en = params->td_en; -+ config_opts.cm_config.ctl_td_mode = params->td_mode; -+ config_opts.cm_config.ctl_cscn_en = params->cscn_en; -+ config_opts.cm_config.ctl_mode = params->mode; -+ config_opts.cm_config.oal = params->oal; -+ config_opts.cm_config.cs_thres.hword = -+ cpu_to_be16(params->cs_thres_in.hword); -+ config_opts.cm_config.cs_thres_x.hword = -+ cpu_to_be16(params->cs_thres_out.hword); -+ config_opts.cm_config.td_thres.hword = -+ cpu_to_be16(params->td_thres.hword); -+ config_opts.cm_config.wr_parm_g.word = -+ cpu_to_be32(params->wr_parm_g.word); -+ config_opts.cm_config.wr_parm_y.word = -+ cpu_to_be32(params->wr_parm_y.word); -+ config_opts.cm_config.wr_parm_r.word = -+ cpu_to_be32(params->wr_parm_r.word); -+ ret = qman_ceetm_configure_ccgr(&config_opts); -+ if (ret) { -+ pr_err("Configure CCGR CM failed!\n"); -+ goto release_lock; -+ } -+ -+ if (we_mask & QM_CCGR_WE_CSCN_EN) -+ if (list_empty(&ccg->cb_node)) -+ list_add(&ccg->cb_node, -+ &p->ccgr_cbs[ccg->parent->dcp_idx]); -+release_lock: -+ spin_unlock_irqrestore(&p->ccgr_lock, irqflags); -+ put_affine_portal(); -+ return ret; -+} -+EXPORT_SYMBOL(qman_ceetm_ccg_set); -+ -+#define CEETM_CCGR_CTL_MASK 0x01 -+int qman_ceetm_ccg_get(struct qm_ceetm_ccg *ccg, -+ struct qm_ceetm_ccg_params *params) -+{ -+ struct qm_mcc_ceetm_ccgr_query query_opts; -+ struct qm_mcr_ceetm_ccgr_query query_result; -+ -+ query_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_QUERY | -+ (ccg->parent->idx << 4) | ccg->idx); -+ query_opts.dcpid = ccg->parent->dcp_idx; -+ -+ if (qman_ceetm_query_ccgr(&query_opts, &query_result)) { -+ pr_err("Can't query CCGR#%d\n", ccg->idx); -+ return -EINVAL; -+ } -+ -+ params->wr_parm_r.word = query_result.cm_query.wr_parm_r.word; -+ params->wr_parm_y.word = query_result.cm_query.wr_parm_y.word; -+ params->wr_parm_g.word = query_result.cm_query.wr_parm_g.word; -+ params->td_thres.hword = query_result.cm_query.td_thres.hword; -+ params->cs_thres_out.hword = query_result.cm_query.cs_thres_x.hword; -+ params->cs_thres_in.hword = query_result.cm_query.cs_thres.hword; -+ params->oal = query_result.cm_query.oal; -+ params->wr_en_g = query_result.cm_query.ctl_wr_en_g; -+ params->wr_en_y = query_result.cm_query.ctl_wr_en_y; -+ params->wr_en_r = query_result.cm_query.ctl_wr_en_r; -+ params->td_en = query_result.cm_query.ctl_td_en; -+ params->td_mode = query_result.cm_query.ctl_td_mode; -+ params->cscn_en = query_result.cm_query.ctl_cscn_en; -+ params->mode = query_result.cm_query.ctl_mode; -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_ccg_get); -+ -+int qman_ceetm_ccg_get_reject_statistics(struct qm_ceetm_ccg *ccg, u32 flags, -+ u64 *frame_count, u64 *byte_count) -+{ -+ struct qm_mcr_ceetm_statistics_query result; -+ u16 cid, command_type; -+ enum qm_dc_portal dcp_idx; -+ int ret; -+ -+ cid = cpu_to_be16((ccg->parent->idx << 4) | ccg->idx); -+ dcp_idx = ccg->parent->dcp_idx; -+ if (flags == QMAN_CEETM_FLAG_CLEAR_STATISTICS_COUNTER) -+ command_type = CEETM_QUERY_REJECT_CLEAR_STATISTICS; -+ else -+ command_type = CEETM_QUERY_REJECT_STATISTICS; -+ -+ ret = qman_ceetm_query_statistics(cid, dcp_idx, command_type, &result); -+ if (ret) { -+ pr_err("Can't query the statistics of CCG#%d!\n", ccg->idx); -+ return -EINVAL; -+ } -+ -+ *frame_count = be40_to_cpu(result.frm_cnt); -+ *byte_count = be48_to_cpu(result.byte_cnt); -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_ccg_get_reject_statistics); -+ -+int qman_ceetm_cscn_swp_get(struct qm_ceetm_ccg *ccg, -+ u16 swp_idx, -+ unsigned int *cscn_enabled) -+{ -+ struct qm_mcc_ceetm_ccgr_query query_opts; -+ struct qm_mcr_ceetm_ccgr_query query_result; -+ int i; -+ -+ DPA_ASSERT(swp_idx < 127); -+ query_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_QUERY | -+ (ccg->parent->idx << 4) | ccg->idx); -+ query_opts.dcpid = ccg->parent->dcp_idx; -+ -+ if (qman_ceetm_query_ccgr(&query_opts, &query_result)) { -+ pr_err("Can't query CCGR#%d\n", ccg->idx); -+ return -EINVAL; -+ } -+ -+ i = swp_idx / 32; -+ i = 3 - i; -+ *cscn_enabled = query_result.cm_query.cscn_targ_swp[i] >> -+ (31 - swp_idx % 32); -+ -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_cscn_swp_get); -+ -+int qman_ceetm_cscn_dcp_set(struct qm_ceetm_ccg *ccg, -+ u16 dcp_idx, -+ u8 vcgid, -+ unsigned int cscn_enabled, -+ u16 we_mask, -+ const struct qm_ceetm_ccg_params *params) -+{ -+ struct qm_mcc_ceetm_ccgr_config config_opts; -+ int ret; -+ -+ config_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_CONFIGURE | -+ (ccg->parent->idx << 4) | ccg->idx); -+ config_opts.dcpid = ccg->parent->dcp_idx; -+ config_opts.we_mask = cpu_to_be16(we_mask | QM_CCGR_WE_CSCN_TUPD | -+ QM_CCGR_WE_CDV); -+ config_opts.cm_config.cdv = vcgid; -+ config_opts.cm_config.cscn_tupd = cpu_to_be16((cscn_enabled << 15) | -+ QM_CGR_TARG_UDP_CTRL_DCP | dcp_idx); -+ config_opts.cm_config.ctl_wr_en_g = params->wr_en_g; -+ config_opts.cm_config.ctl_wr_en_y = params->wr_en_y; -+ config_opts.cm_config.ctl_wr_en_r = params->wr_en_r; -+ config_opts.cm_config.ctl_td_en = params->td_en; -+ config_opts.cm_config.ctl_td_mode = params->td_mode; -+ config_opts.cm_config.ctl_cscn_en = params->cscn_en; -+ config_opts.cm_config.ctl_mode = params->mode; -+ config_opts.cm_config.cs_thres.hword = -+ cpu_to_be16(params->cs_thres_in.hword); -+ config_opts.cm_config.cs_thres_x.hword = -+ cpu_to_be16(params->cs_thres_out.hword); -+ config_opts.cm_config.td_thres.hword = -+ cpu_to_be16(params->td_thres.hword); -+ config_opts.cm_config.wr_parm_g.word = -+ cpu_to_be32(params->wr_parm_g.word); -+ config_opts.cm_config.wr_parm_y.word = -+ cpu_to_be32(params->wr_parm_y.word); -+ config_opts.cm_config.wr_parm_r.word = -+ cpu_to_be32(params->wr_parm_r.word); -+ -+ ret = qman_ceetm_configure_ccgr(&config_opts); -+ if (ret) { -+ pr_err("Configure CSCN_TARG_DCP failed!\n"); -+ return -EINVAL; -+ } -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_cscn_dcp_set); -+ -+int qman_ceetm_cscn_dcp_get(struct qm_ceetm_ccg *ccg, -+ u16 dcp_idx, -+ u8 *vcgid, -+ unsigned int *cscn_enabled) -+{ -+ struct qm_mcc_ceetm_ccgr_query query_opts; -+ struct qm_mcr_ceetm_ccgr_query query_result; -+ -+ query_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_QUERY | -+ (ccg->parent->idx << 4) | ccg->idx); -+ query_opts.dcpid = ccg->parent->dcp_idx; -+ -+ if (qman_ceetm_query_ccgr(&query_opts, &query_result)) { -+ pr_err("Can't query CCGR#%d\n", ccg->idx); -+ return -EINVAL; -+ } -+ -+ *vcgid = query_result.cm_query.cdv; -+ *cscn_enabled = (query_result.cm_query.cscn_targ_dcp >> dcp_idx) & 0x1; -+ return 0; -+} -+EXPORT_SYMBOL(qman_ceetm_cscn_dcp_get); -+ -+int qman_ceetm_querycongestion(struct __qm_mcr_querycongestion *ccg_state, -+ unsigned int dcp_idx) -+{ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ u8 res; -+ int i, j; -+ -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ -+ mcc = qm_mc_start(&p->p); -+ for (i = 0; i < 2; i++) { -+ mcc->ccgr_query.ccgrid = -+ cpu_to_be16(CEETM_QUERY_CONGESTION_STATE | i); -+ mcc->ccgr_query.dcpid = dcp_idx; -+ qm_mc_commit(&p->p, QM_CEETM_VERB_CCGR_QUERY); -+ -+ while (!(mcr = qm_mc_result(&p->p))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_CEETM_VERB_CCGR_QUERY); -+ res = mcr->result; -+ if (res == QM_MCR_RESULT_OK) { -+ for (j = 0; j < 8; j++) -+ mcr->ccgr_query.congestion_state.state. -+ __state[j] = be32_to_cpu(mcr->ccgr_query. -+ congestion_state.state.__state[j]); -+ *(ccg_state + i) = -+ mcr->ccgr_query.congestion_state.state; -+ } else { -+ pr_err("QUERY CEETM CONGESTION STATE failed\n"); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ return -EIO; -+ } -+ } -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return 0; -+} -+ -+int qman_set_wpm(int wpm_enable) -+{ -+ return qm_set_wpm(wpm_enable); -+} -+EXPORT_SYMBOL(qman_set_wpm); -+ -+int qman_get_wpm(int *wpm_enable) -+{ -+ return qm_get_wpm(wpm_enable); -+} -+EXPORT_SYMBOL(qman_get_wpm); -+ -+int qman_shutdown_fq(u32 fqid) -+{ -+ struct qman_portal *p; -+ unsigned long irqflags __maybe_unused; -+ int ret; -+ struct qm_portal *low_p; -+ p = get_affine_portal(); -+ PORTAL_IRQ_LOCK(p, irqflags); -+ low_p = &p->p; -+ ret = qm_shutdown_fq(&low_p, 1, fqid); -+ PORTAL_IRQ_UNLOCK(p, irqflags); -+ put_affine_portal(); -+ return ret; -+} -+ -+const struct qm_portal_config *qman_get_qm_portal_config( -+ struct qman_portal *portal) -+{ -+ return portal->sharing_redirect ? NULL : portal->config; -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_low.h -@@ -0,0 +1,1427 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "qman_private.h" -+ -+/***************************/ -+/* Portal register assists */ -+/***************************/ -+ -+/* Cache-inhibited register offsets */ -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ -+#define QM_REG_EQCR_PI_CINH 0x0000 -+#define QM_REG_EQCR_CI_CINH 0x0004 -+#define QM_REG_EQCR_ITR 0x0008 -+#define QM_REG_DQRR_PI_CINH 0x0040 -+#define QM_REG_DQRR_CI_CINH 0x0044 -+#define QM_REG_DQRR_ITR 0x0048 -+#define QM_REG_DQRR_DCAP 0x0050 -+#define QM_REG_DQRR_SDQCR 0x0054 -+#define QM_REG_DQRR_VDQCR 0x0058 -+#define QM_REG_DQRR_PDQCR 0x005c -+#define QM_REG_MR_PI_CINH 0x0080 -+#define QM_REG_MR_CI_CINH 0x0084 -+#define QM_REG_MR_ITR 0x0088 -+#define QM_REG_CFG 0x0100 -+#define QM_REG_ISR 0x0e00 -+#define QM_REG_IIR 0x0e0c -+#define QM_REG_ITPR 0x0e14 -+ -+/* Cache-enabled register offsets */ -+#define QM_CL_EQCR 0x0000 -+#define QM_CL_DQRR 0x1000 -+#define QM_CL_MR 0x2000 -+#define QM_CL_EQCR_PI_CENA 0x3000 -+#define QM_CL_EQCR_CI_CENA 0x3100 -+#define QM_CL_DQRR_PI_CENA 0x3200 -+#define QM_CL_DQRR_CI_CENA 0x3300 -+#define QM_CL_MR_PI_CENA 0x3400 -+#define QM_CL_MR_CI_CENA 0x3500 -+#define QM_CL_CR 0x3800 -+#define QM_CL_RR0 0x3900 -+#define QM_CL_RR1 0x3940 -+ -+#endif -+ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ -+#define QM_REG_EQCR_PI_CINH 0x3000 -+#define QM_REG_EQCR_CI_CINH 0x3040 -+#define QM_REG_EQCR_ITR 0x3080 -+#define QM_REG_DQRR_PI_CINH 0x3100 -+#define QM_REG_DQRR_CI_CINH 0x3140 -+#define QM_REG_DQRR_ITR 0x3180 -+#define QM_REG_DQRR_DCAP 0x31C0 -+#define QM_REG_DQRR_SDQCR 0x3200 -+#define QM_REG_DQRR_VDQCR 0x3240 -+#define QM_REG_DQRR_PDQCR 0x3280 -+#define QM_REG_MR_PI_CINH 0x3300 -+#define QM_REG_MR_CI_CINH 0x3340 -+#define QM_REG_MR_ITR 0x3380 -+#define QM_REG_CFG 0x3500 -+#define QM_REG_ISR 0x3600 -+#define QM_REG_IIR 0x36C0 -+#define QM_REG_ITPR 0x3740 -+ -+/* Cache-enabled register offsets */ -+#define QM_CL_EQCR 0x0000 -+#define QM_CL_DQRR 0x1000 -+#define QM_CL_MR 0x2000 -+#define QM_CL_EQCR_PI_CENA 0x3000 -+#define QM_CL_EQCR_CI_CENA 0x3040 -+#define QM_CL_DQRR_PI_CENA 0x3100 -+#define QM_CL_DQRR_CI_CENA 0x3140 -+#define QM_CL_MR_PI_CENA 0x3300 -+#define QM_CL_MR_CI_CENA 0x3340 -+#define QM_CL_CR 0x3800 -+#define QM_CL_RR0 0x3900 -+#define QM_CL_RR1 0x3940 -+ -+#endif -+ -+ -+/* BTW, the drivers (and h/w programming model) already obtain the required -+ * synchronisation for portal accesses via lwsync(), hwsync(), and -+ * data-dependencies. Use of barrier()s or other order-preserving primitives -+ * simply degrade performance. Hence the use of the __raw_*() interfaces, which -+ * simply ensure that the compiler treats the portal registers as volatile (ie. -+ * non-coherent). */ -+ -+/* Cache-inhibited register access. */ -+#define __qm_in(qm, o) be32_to_cpu(__raw_readl((qm)->addr_ci + (o))) -+#define __qm_out(qm, o, val) __raw_writel((cpu_to_be32(val)), \ -+ (qm)->addr_ci + (o)); -+#define qm_in(reg) __qm_in(&portal->addr, QM_REG_##reg) -+#define qm_out(reg, val) __qm_out(&portal->addr, QM_REG_##reg, val) -+ -+/* Cache-enabled (index) register access */ -+#define __qm_cl_touch_ro(qm, o) dcbt_ro((qm)->addr_ce + (o)) -+#define __qm_cl_touch_rw(qm, o) dcbt_rw((qm)->addr_ce + (o)) -+#define __qm_cl_in(qm, o) be32_to_cpu(__raw_readl((qm)->addr_ce + (o))) -+#define __qm_cl_out(qm, o, val) \ -+ do { \ -+ u32 *__tmpclout = (qm)->addr_ce + (o); \ -+ __raw_writel(cpu_to_be32(val), __tmpclout); \ -+ dcbf(__tmpclout); \ -+ } while (0) -+#define __qm_cl_invalidate(qm, o) dcbi((qm)->addr_ce + (o)) -+#define qm_cl_touch_ro(reg) __qm_cl_touch_ro(&portal->addr, QM_CL_##reg##_CENA) -+#define qm_cl_touch_rw(reg) __qm_cl_touch_rw(&portal->addr, QM_CL_##reg##_CENA) -+#define qm_cl_in(reg) __qm_cl_in(&portal->addr, QM_CL_##reg##_CENA) -+#define qm_cl_out(reg, val) __qm_cl_out(&portal->addr, QM_CL_##reg##_CENA, val) -+#define qm_cl_invalidate(reg)\ -+ __qm_cl_invalidate(&portal->addr, QM_CL_##reg##_CENA) -+ -+/* Cache-enabled ring access */ -+#define qm_cl(base, idx) ((void *)base + ((idx) << 6)) -+ -+/* Cyclic helper for rings. FIXME: once we are able to do fine-grain perf -+ * analysis, look at using the "extra" bit in the ring index registers to avoid -+ * cyclic issues. */ -+static inline u8 qm_cyc_diff(u8 ringsize, u8 first, u8 last) -+{ -+ /* 'first' is included, 'last' is excluded */ -+ if (first <= last) -+ return last - first; -+ return ringsize + last - first; -+} -+ -+/* Portal modes. -+ * Enum types; -+ * pmode == production mode -+ * cmode == consumption mode, -+ * dmode == h/w dequeue mode. -+ * Enum values use 3 letter codes. First letter matches the portal mode, -+ * remaining two letters indicate; -+ * ci == cache-inhibited portal register -+ * ce == cache-enabled portal register -+ * vb == in-band valid-bit (cache-enabled) -+ * dc == DCA (Discrete Consumption Acknowledgement), DQRR-only -+ * As for "enum qm_dqrr_dmode", it should be self-explanatory. -+ */ -+enum qm_eqcr_pmode { /* matches QCSP_CFG::EPM */ -+ qm_eqcr_pci = 0, /* PI index, cache-inhibited */ -+ qm_eqcr_pce = 1, /* PI index, cache-enabled */ -+ qm_eqcr_pvb = 2 /* valid-bit */ -+}; -+enum qm_dqrr_dmode { /* matches QCSP_CFG::DP */ -+ qm_dqrr_dpush = 0, /* SDQCR + VDQCR */ -+ qm_dqrr_dpull = 1 /* PDQCR */ -+}; -+enum qm_dqrr_pmode { /* s/w-only */ -+ qm_dqrr_pci, /* reads DQRR_PI_CINH */ -+ qm_dqrr_pce, /* reads DQRR_PI_CENA */ -+ qm_dqrr_pvb /* reads valid-bit */ -+}; -+enum qm_dqrr_cmode { /* matches QCSP_CFG::DCM */ -+ qm_dqrr_cci = 0, /* CI index, cache-inhibited */ -+ qm_dqrr_cce = 1, /* CI index, cache-enabled */ -+ qm_dqrr_cdc = 2 /* Discrete Consumption Acknowledgement */ -+}; -+enum qm_mr_pmode { /* s/w-only */ -+ qm_mr_pci, /* reads MR_PI_CINH */ -+ qm_mr_pce, /* reads MR_PI_CENA */ -+ qm_mr_pvb /* reads valid-bit */ -+}; -+enum qm_mr_cmode { /* matches QCSP_CFG::MM */ -+ qm_mr_cci = 0, /* CI index, cache-inhibited */ -+ qm_mr_cce = 1 /* CI index, cache-enabled */ -+}; -+ -+ -+/* ------------------------- */ -+/* --- Portal structures --- */ -+ -+#define QM_EQCR_SIZE 8 -+#define QM_DQRR_SIZE 16 -+#define QM_MR_SIZE 8 -+ -+struct qm_eqcr { -+ struct qm_eqcr_entry *ring, *cursor; -+ u8 ci, available, ithresh, vbit; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ u32 busy; -+ enum qm_eqcr_pmode pmode; -+#endif -+}; -+ -+struct qm_dqrr { -+ const struct qm_dqrr_entry *ring, *cursor; -+ u8 pi, ci, fill, ithresh, vbit; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ enum qm_dqrr_dmode dmode; -+ enum qm_dqrr_pmode pmode; -+ enum qm_dqrr_cmode cmode; -+#endif -+}; -+ -+struct qm_mr { -+ const struct qm_mr_entry *ring, *cursor; -+ u8 pi, ci, fill, ithresh, vbit; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ enum qm_mr_pmode pmode; -+ enum qm_mr_cmode cmode; -+#endif -+}; -+ -+struct qm_mc { -+ struct qm_mc_command *cr; -+ struct qm_mc_result *rr; -+ u8 rridx, vbit; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ enum { -+ /* Can be _mc_start()ed */ -+ qman_mc_idle, -+ /* Can be _mc_commit()ed or _mc_abort()ed */ -+ qman_mc_user, -+ /* Can only be _mc_retry()ed */ -+ qman_mc_hw -+ } state; -+#endif -+}; -+ -+#define QM_PORTAL_ALIGNMENT ____cacheline_aligned -+ -+struct qm_addr { -+ void __iomem *addr_ce; /* cache-enabled */ -+ void __iomem *addr_ci; /* cache-inhibited */ -+}; -+ -+struct qm_portal { -+ /* In the non-CONFIG_FSL_DPA_CHECKING case, the following stuff up to -+ * and including 'mc' fits within a cacheline (yay!). The 'config' part -+ * is setup-only, so isn't a cause for a concern. In other words, don't -+ * rearrange this structure on a whim, there be dragons ... */ -+ struct qm_addr addr; -+ struct qm_eqcr eqcr; -+ struct qm_dqrr dqrr; -+ struct qm_mr mr; -+ struct qm_mc mc; -+} QM_PORTAL_ALIGNMENT; -+ -+ -+/* ---------------- */ -+/* --- EQCR API --- */ -+ -+/* Bit-wise logic to wrap a ring pointer by clearing the "carry bit" */ -+#define EQCR_CARRYCLEAR(p) \ -+ (void *)((unsigned long)(p) & (~(unsigned long)(QM_EQCR_SIZE << 6))) -+ -+/* Bit-wise logic to convert a ring pointer to a ring index */ -+static inline u8 EQCR_PTR2IDX(struct qm_eqcr_entry *e) -+{ -+ return ((uintptr_t)e >> 6) & (QM_EQCR_SIZE - 1); -+} -+ -+/* Increment the 'cursor' ring pointer, taking 'vbit' into account */ -+static inline void EQCR_INC(struct qm_eqcr *eqcr) -+{ -+ /* NB: this is odd-looking, but experiments show that it generates fast -+ * code with essentially no branching overheads. We increment to the -+ * next EQCR pointer and handle overflow and 'vbit'. */ -+ struct qm_eqcr_entry *partial = eqcr->cursor + 1; -+ eqcr->cursor = EQCR_CARRYCLEAR(partial); -+ if (partial != eqcr->cursor) -+ eqcr->vbit ^= QM_EQCR_VERB_VBIT; -+} -+ -+static inline int qm_eqcr_init(struct qm_portal *portal, -+ enum qm_eqcr_pmode pmode, -+ unsigned int eq_stash_thresh, -+ int eq_stash_prio) -+{ -+ /* This use of 'register', as well as all other occurrences, is because -+ * it has been observed to generate much faster code with gcc than is -+ * otherwise the case. */ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ u32 cfg; -+ u8 pi; -+ -+ eqcr->ring = portal->addr.addr_ce + QM_CL_EQCR; -+ eqcr->ci = qm_in(EQCR_CI_CINH) & (QM_EQCR_SIZE - 1); -+ qm_cl_invalidate(EQCR_CI); -+ pi = qm_in(EQCR_PI_CINH) & (QM_EQCR_SIZE - 1); -+ eqcr->cursor = eqcr->ring + pi; -+ eqcr->vbit = (qm_in(EQCR_PI_CINH) & QM_EQCR_SIZE) ? -+ QM_EQCR_VERB_VBIT : 0; -+ eqcr->available = QM_EQCR_SIZE - 1 - -+ qm_cyc_diff(QM_EQCR_SIZE, eqcr->ci, pi); -+ eqcr->ithresh = qm_in(EQCR_ITR); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ eqcr->busy = 0; -+ eqcr->pmode = pmode; -+#endif -+ cfg = (qm_in(CFG) & 0x00ffffff) | -+ (eq_stash_thresh << 28) | /* QCSP_CFG: EST */ -+ (eq_stash_prio << 26) | /* QCSP_CFG: EP */ -+ ((pmode & 0x3) << 24); /* QCSP_CFG::EPM */ -+ qm_out(CFG, cfg); -+ return 0; -+} -+ -+static inline unsigned int qm_eqcr_get_ci_stashing(struct qm_portal *portal) -+{ -+ return (qm_in(CFG) >> 28) & 0x7; -+} -+ -+static inline void qm_eqcr_finish(struct qm_portal *portal) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ u8 pi, ci; -+ u32 cfg; -+ -+ /* -+ * Disable EQCI stashing because the QMan only -+ * presents the value it previously stashed to -+ * maintain coherency. Setting the stash threshold -+ * to 1 then 0 ensures that QMan has resyncronized -+ * its internal copy so that the portal is clean -+ * when it is reinitialized in the future -+ */ -+ cfg = (qm_in(CFG) & 0x0fffffff) | -+ (1 << 28); /* QCSP_CFG: EST */ -+ qm_out(CFG, cfg); -+ cfg &= 0x0fffffff; /* stash threshold = 0 */ -+ qm_out(CFG, cfg); -+ -+ pi = qm_in(EQCR_PI_CINH) & (QM_EQCR_SIZE - 1); -+ ci = qm_in(EQCR_CI_CINH) & (QM_EQCR_SIZE - 1); -+ -+ /* Refresh EQCR CI cache value */ -+ qm_cl_invalidate(EQCR_CI); -+ eqcr->ci = qm_cl_in(EQCR_CI) & (QM_EQCR_SIZE - 1); -+ -+ DPA_ASSERT(!eqcr->busy); -+ if (pi != EQCR_PTR2IDX(eqcr->cursor)) -+ pr_crit("losing uncommited EQCR entries\n"); -+ if (ci != eqcr->ci) -+ pr_crit("missing existing EQCR completions\n"); -+ if (eqcr->ci != EQCR_PTR2IDX(eqcr->cursor)) -+ pr_crit("EQCR destroyed unquiesced\n"); -+} -+ -+static inline struct qm_eqcr_entry *qm_eqcr_start_no_stash(struct qm_portal -+ *portal) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ DPA_ASSERT(!eqcr->busy); -+ if (!eqcr->available) -+ return NULL; -+ -+ -+#ifdef CONFIG_FSL_DPA_CHECKING -+ eqcr->busy = 1; -+#endif -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ dcbz_64(eqcr->cursor); -+#endif -+ return eqcr->cursor; -+} -+ -+static inline struct qm_eqcr_entry *qm_eqcr_start_stash(struct qm_portal -+ *portal) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ u8 diff, old_ci; -+ -+ DPA_ASSERT(!eqcr->busy); -+ if (!eqcr->available) { -+ old_ci = eqcr->ci; -+ eqcr->ci = qm_cl_in(EQCR_CI) & (QM_EQCR_SIZE - 1); -+ diff = qm_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci); -+ eqcr->available += diff; -+ if (!diff) -+ return NULL; -+ } -+#ifdef CONFIG_FSL_DPA_CHECKING -+ eqcr->busy = 1; -+#endif -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ dcbz_64(eqcr->cursor); -+#endif -+ return eqcr->cursor; -+} -+ -+static inline void qm_eqcr_abort(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_eqcr *eqcr = &portal->eqcr; -+ DPA_ASSERT(eqcr->busy); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ eqcr->busy = 0; -+#endif -+} -+ -+static inline struct qm_eqcr_entry *qm_eqcr_pend_and_next( -+ struct qm_portal *portal, u8 myverb) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ DPA_ASSERT(eqcr->busy); -+ DPA_ASSERT(eqcr->pmode != qm_eqcr_pvb); -+ if (eqcr->available == 1) -+ return NULL; -+ eqcr->cursor->__dont_write_directly__verb = myverb | eqcr->vbit; -+ dcbf(eqcr->cursor); -+ EQCR_INC(eqcr); -+ eqcr->available--; -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ dcbz_64(eqcr->cursor); -+#endif -+ return eqcr->cursor; -+} -+ -+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -+#define EQCR_COMMIT_CHECKS(eqcr) \ -+do { \ -+ DPA_ASSERT(eqcr->busy); \ -+ DPA_ASSERT(eqcr->cursor->orp == (eqcr->cursor->orp & 0xffffff00)); \ -+ DPA_ASSERT(eqcr->cursor->fqid == (eqcr->cursor->fqid & 0xffffff00)); \ -+} while (0) -+#else -+#define EQCR_COMMIT_CHECKS(eqcr) \ -+do { \ -+ DPA_ASSERT(eqcr->busy); \ -+ DPA_ASSERT(eqcr->cursor->orp == (eqcr->cursor->orp & \ -+ cpu_to_be32(0x00ffffff))); \ -+ DPA_ASSERT(eqcr->cursor->fqid == (eqcr->cursor->fqid & \ -+ cpu_to_be32(0x00ffffff))); \ -+} while (0) -+#endif -+ -+static inline void qm_eqcr_pci_commit(struct qm_portal *portal, u8 myverb) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ EQCR_COMMIT_CHECKS(eqcr); -+ DPA_ASSERT(eqcr->pmode == qm_eqcr_pci); -+ eqcr->cursor->__dont_write_directly__verb = myverb | eqcr->vbit; -+ EQCR_INC(eqcr); -+ eqcr->available--; -+ dcbf(eqcr->cursor); -+ hwsync(); -+ qm_out(EQCR_PI_CINH, EQCR_PTR2IDX(eqcr->cursor)); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ eqcr->busy = 0; -+#endif -+} -+ -+static inline void qm_eqcr_pce_prefetch(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_eqcr *eqcr = &portal->eqcr; -+ DPA_ASSERT(eqcr->pmode == qm_eqcr_pce); -+ qm_cl_invalidate(EQCR_PI); -+ qm_cl_touch_rw(EQCR_PI); -+} -+ -+static inline void qm_eqcr_pce_commit(struct qm_portal *portal, u8 myverb) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ EQCR_COMMIT_CHECKS(eqcr); -+ DPA_ASSERT(eqcr->pmode == qm_eqcr_pce); -+ eqcr->cursor->__dont_write_directly__verb = myverb | eqcr->vbit; -+ EQCR_INC(eqcr); -+ eqcr->available--; -+ dcbf(eqcr->cursor); -+ lwsync(); -+ qm_cl_out(EQCR_PI, EQCR_PTR2IDX(eqcr->cursor)); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ eqcr->busy = 0; -+#endif -+} -+ -+static inline void qm_eqcr_pvb_commit(struct qm_portal *portal, u8 myverb) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ struct qm_eqcr_entry *eqcursor; -+ EQCR_COMMIT_CHECKS(eqcr); -+ DPA_ASSERT(eqcr->pmode == qm_eqcr_pvb); -+ lwsync(); -+ eqcursor = eqcr->cursor; -+ eqcursor->__dont_write_directly__verb = myverb | eqcr->vbit; -+ dcbf(eqcursor); -+ EQCR_INC(eqcr); -+ eqcr->available--; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ eqcr->busy = 0; -+#endif -+} -+ -+static inline u8 qm_eqcr_cci_update(struct qm_portal *portal) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ u8 diff, old_ci = eqcr->ci; -+ eqcr->ci = qm_in(EQCR_CI_CINH) & (QM_EQCR_SIZE - 1); -+ diff = qm_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci); -+ eqcr->available += diff; -+ return diff; -+} -+ -+static inline void qm_eqcr_cce_prefetch(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_eqcr *eqcr = &portal->eqcr; -+ qm_cl_touch_ro(EQCR_CI); -+} -+ -+static inline u8 qm_eqcr_cce_update(struct qm_portal *portal) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ u8 diff, old_ci = eqcr->ci; -+ eqcr->ci = qm_cl_in(EQCR_CI) & (QM_EQCR_SIZE - 1); -+ qm_cl_invalidate(EQCR_CI); -+ diff = qm_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci); -+ eqcr->available += diff; -+ return diff; -+} -+ -+static inline u8 qm_eqcr_get_ithresh(struct qm_portal *portal) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ return eqcr->ithresh; -+} -+ -+static inline void qm_eqcr_set_ithresh(struct qm_portal *portal, u8 ithresh) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ eqcr->ithresh = ithresh; -+ qm_out(EQCR_ITR, ithresh); -+} -+ -+static inline u8 qm_eqcr_get_avail(struct qm_portal *portal) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ return eqcr->available; -+} -+ -+static inline u8 qm_eqcr_get_fill(struct qm_portal *portal) -+{ -+ register struct qm_eqcr *eqcr = &portal->eqcr; -+ return QM_EQCR_SIZE - 1 - eqcr->available; -+} -+ -+ -+/* ---------------- */ -+/* --- DQRR API --- */ -+ -+/* FIXME: many possible improvements; -+ * - look at changing the API to use pointer rather than index parameters now -+ * that 'cursor' is a pointer, -+ * - consider moving other parameters to pointer if it could help (ci) -+ */ -+ -+#define DQRR_CARRYCLEAR(p) \ -+ (void *)((unsigned long)(p) & (~(unsigned long)(QM_DQRR_SIZE << 6))) -+ -+static inline u8 DQRR_PTR2IDX(const struct qm_dqrr_entry *e) -+{ -+ return ((uintptr_t)e >> 6) & (QM_DQRR_SIZE - 1); -+} -+ -+static inline const struct qm_dqrr_entry *DQRR_INC( -+ const struct qm_dqrr_entry *e) -+{ -+ return DQRR_CARRYCLEAR(e + 1); -+} -+ -+static inline void qm_dqrr_set_maxfill(struct qm_portal *portal, u8 mf) -+{ -+ qm_out(CFG, (qm_in(CFG) & 0xff0fffff) | -+ ((mf & (QM_DQRR_SIZE - 1)) << 20)); -+} -+ -+static inline void qm_dqrr_cci_consume(struct qm_portal *portal, u8 num) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cci); -+ dqrr->ci = (dqrr->ci + num) & (QM_DQRR_SIZE - 1); -+ qm_out(DQRR_CI_CINH, dqrr->ci); -+} -+ -+static inline void qm_dqrr_cce_consume(struct qm_portal *portal, u8 num) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cce); -+ dqrr->ci = (dqrr->ci + num) & (QM_DQRR_SIZE - 1); -+ qm_cl_out(DQRR_CI, dqrr->ci); -+} -+ -+static inline void qm_dqrr_cdc_consume_n(struct qm_portal *portal, u16 bitmask) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc); -+ qm_out(DQRR_DCAP, (1 << 8) | /* DQRR_DCAP::S */ -+ ((u32)bitmask << 16)); /* DQRR_DCAP::DCAP_CI */ -+ dqrr->ci = qm_in(DQRR_CI_CINH) & (QM_DQRR_SIZE - 1); -+ dqrr->fill = qm_cyc_diff(QM_DQRR_SIZE, dqrr->ci, dqrr->pi); -+} -+ -+static inline int qm_dqrr_init(struct qm_portal *portal, -+ const struct qm_portal_config *config, -+ enum qm_dqrr_dmode dmode, -+ __maybe_unused enum qm_dqrr_pmode pmode, -+ enum qm_dqrr_cmode cmode, u8 max_fill) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ u32 cfg; -+ -+ /* Make sure the DQRR will be idle when we enable */ -+ qm_out(DQRR_SDQCR, 0); -+ qm_out(DQRR_VDQCR, 0); -+ qm_out(DQRR_PDQCR, 0); -+ dqrr->ring = portal->addr.addr_ce + QM_CL_DQRR; -+ dqrr->pi = qm_in(DQRR_PI_CINH) & (QM_DQRR_SIZE - 1); -+ dqrr->ci = qm_in(DQRR_CI_CINH) & (QM_DQRR_SIZE - 1); -+ dqrr->cursor = dqrr->ring + dqrr->ci; -+ dqrr->fill = qm_cyc_diff(QM_DQRR_SIZE, dqrr->ci, dqrr->pi); -+ dqrr->vbit = (qm_in(DQRR_PI_CINH) & QM_DQRR_SIZE) ? -+ QM_DQRR_VERB_VBIT : 0; -+ dqrr->ithresh = qm_in(DQRR_ITR); -+ -+ /* Free up pending DQRR entries if any as per current DCM */ -+ if (dqrr->fill) { -+ enum qm_dqrr_cmode dcm = (qm_in(CFG) >> 16) & 3; -+ -+#ifdef CONFIG_FSL_DPA_CHECKING -+ dqrr->cmode = dcm; -+#endif -+ switch (dcm) { -+ case qm_dqrr_cci: -+ qm_dqrr_cci_consume(portal, dqrr->fill); -+ break; -+ case qm_dqrr_cce: -+ qm_dqrr_cce_consume(portal, dqrr->fill); -+ break; -+ case qm_dqrr_cdc: -+ qm_dqrr_cdc_consume_n(portal, (QM_DQRR_SIZE - 1)); -+ break; -+ default: -+ DPA_ASSERT(0); -+ } -+ } -+ -+#ifdef CONFIG_FSL_DPA_CHECKING -+ dqrr->dmode = dmode; -+ dqrr->pmode = pmode; -+ dqrr->cmode = cmode; -+#endif -+ /* Invalidate every ring entry before beginning */ -+ for (cfg = 0; cfg < QM_DQRR_SIZE; cfg++) -+ dcbi(qm_cl(dqrr->ring, cfg)); -+ cfg = (qm_in(CFG) & 0xff000f00) | -+ ((max_fill & (QM_DQRR_SIZE - 1)) << 20) | /* DQRR_MF */ -+ ((dmode & 1) << 18) | /* DP */ -+ ((cmode & 3) << 16) | /* DCM */ -+ 0xa0 | /* RE+SE */ -+ (0 ? 0x40 : 0) | /* Ignore RP */ -+ (0 ? 0x10 : 0); /* Ignore SP */ -+ qm_out(CFG, cfg); -+ qm_dqrr_set_maxfill(portal, max_fill); -+ return 0; -+} -+ -+static inline void qm_dqrr_finish(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if ((dqrr->cmode != qm_dqrr_cdc) && -+ (dqrr->ci != DQRR_PTR2IDX(dqrr->cursor))) -+ pr_crit("Ignoring completed DQRR entries\n"); -+#endif -+} -+ -+static inline const struct qm_dqrr_entry *qm_dqrr_current( -+ struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ if (!dqrr->fill) -+ return NULL; -+ return dqrr->cursor; -+} -+ -+static inline u8 qm_dqrr_cursor(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ return DQRR_PTR2IDX(dqrr->cursor); -+} -+ -+static inline u8 qm_dqrr_next(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->fill); -+ dqrr->cursor = DQRR_INC(dqrr->cursor); -+ return --dqrr->fill; -+} -+ -+static inline u8 qm_dqrr_pci_update(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ u8 diff, old_pi = dqrr->pi; -+ DPA_ASSERT(dqrr->pmode == qm_dqrr_pci); -+ dqrr->pi = qm_in(DQRR_PI_CINH) & (QM_DQRR_SIZE - 1); -+ diff = qm_cyc_diff(QM_DQRR_SIZE, old_pi, dqrr->pi); -+ dqrr->fill += diff; -+ return diff; -+} -+ -+static inline void qm_dqrr_pce_prefetch(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->pmode == qm_dqrr_pce); -+ qm_cl_invalidate(DQRR_PI); -+ qm_cl_touch_ro(DQRR_PI); -+} -+ -+static inline u8 qm_dqrr_pce_update(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ u8 diff, old_pi = dqrr->pi; -+ DPA_ASSERT(dqrr->pmode == qm_dqrr_pce); -+ dqrr->pi = qm_cl_in(DQRR_PI) & (QM_DQRR_SIZE - 1); -+ diff = qm_cyc_diff(QM_DQRR_SIZE, old_pi, dqrr->pi); -+ dqrr->fill += diff; -+ return diff; -+} -+ -+static inline void qm_dqrr_pvb_update(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ const struct qm_dqrr_entry *res = qm_cl(dqrr->ring, dqrr->pi); -+ DPA_ASSERT(dqrr->pmode == qm_dqrr_pvb); -+#if (defined CONFIG_PPC || defined CONFIG_PPC64) && !defined CONFIG_FSL_PAMU -+ /* -+ * On PowerPC platforms if PAMU is not available we need to -+ * manually invalidate the cache. When PAMU is available the -+ * cache is updated by stashing operations generated by QMan -+ */ -+ dcbi(res); -+ dcbt_ro(res); -+#endif -+ -+ /* when accessing 'verb', use __raw_readb() to ensure that compiler -+ * inlining doesn't try to optimise out "excess reads". */ -+ if ((__raw_readb(&res->verb) & QM_DQRR_VERB_VBIT) == dqrr->vbit) { -+ dqrr->pi = (dqrr->pi + 1) & (QM_DQRR_SIZE - 1); -+ if (!dqrr->pi) -+ dqrr->vbit ^= QM_DQRR_VERB_VBIT; -+ dqrr->fill++; -+ } -+} -+ -+ -+static inline void qm_dqrr_cci_consume_to_current(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cci); -+ dqrr->ci = DQRR_PTR2IDX(dqrr->cursor); -+ qm_out(DQRR_CI_CINH, dqrr->ci); -+} -+ -+static inline void qm_dqrr_cce_prefetch(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cce); -+ qm_cl_invalidate(DQRR_CI); -+ qm_cl_touch_rw(DQRR_CI); -+} -+ -+static inline void qm_dqrr_cce_consume_to_current(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cce); -+ dqrr->ci = DQRR_PTR2IDX(dqrr->cursor); -+ qm_cl_out(DQRR_CI, dqrr->ci); -+} -+ -+static inline void qm_dqrr_cdc_consume_1(struct qm_portal *portal, u8 idx, -+ int park) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc); -+ DPA_ASSERT(idx < QM_DQRR_SIZE); -+ qm_out(DQRR_DCAP, (0 << 8) | /* S */ -+ ((park ? 1 : 0) << 6) | /* PK */ -+ idx); /* DCAP_CI */ -+} -+ -+static inline void qm_dqrr_cdc_consume_1ptr(struct qm_portal *portal, -+ const struct qm_dqrr_entry *dq, -+ int park) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ u8 idx = DQRR_PTR2IDX(dq); -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc); -+ DPA_ASSERT((dqrr->ring + idx) == dq); -+ DPA_ASSERT(idx < QM_DQRR_SIZE); -+ qm_out(DQRR_DCAP, (0 << 8) | /* DQRR_DCAP::S */ -+ ((park ? 1 : 0) << 6) | /* DQRR_DCAP::PK */ -+ idx); /* DQRR_DCAP::DCAP_CI */ -+} -+ -+static inline u8 qm_dqrr_cdc_cci(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc); -+ return qm_in(DQRR_CI_CINH) & (QM_DQRR_SIZE - 1); -+} -+ -+static inline void qm_dqrr_cdc_cce_prefetch(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc); -+ qm_cl_invalidate(DQRR_CI); -+ qm_cl_touch_ro(DQRR_CI); -+} -+ -+static inline u8 qm_dqrr_cdc_cce(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc); -+ return qm_cl_in(DQRR_CI) & (QM_DQRR_SIZE - 1); -+} -+ -+static inline u8 qm_dqrr_get_ci(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode != qm_dqrr_cdc); -+ return dqrr->ci; -+} -+ -+static inline void qm_dqrr_park(struct qm_portal *portal, u8 idx) -+{ -+ __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode != qm_dqrr_cdc); -+ qm_out(DQRR_DCAP, (0 << 8) | /* S */ -+ (1 << 6) | /* PK */ -+ (idx & (QM_DQRR_SIZE - 1))); /* DCAP_CI */ -+} -+ -+static inline void qm_dqrr_park_current(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ DPA_ASSERT(dqrr->cmode != qm_dqrr_cdc); -+ qm_out(DQRR_DCAP, (0 << 8) | /* S */ -+ (1 << 6) | /* PK */ -+ DQRR_PTR2IDX(dqrr->cursor)); /* DCAP_CI */ -+} -+ -+static inline void qm_dqrr_sdqcr_set(struct qm_portal *portal, u32 sdqcr) -+{ -+ qm_out(DQRR_SDQCR, sdqcr); -+} -+ -+static inline u32 qm_dqrr_sdqcr_get(struct qm_portal *portal) -+{ -+ return qm_in(DQRR_SDQCR); -+} -+ -+static inline void qm_dqrr_vdqcr_set(struct qm_portal *portal, u32 vdqcr) -+{ -+ qm_out(DQRR_VDQCR, vdqcr); -+} -+ -+static inline u32 qm_dqrr_vdqcr_get(struct qm_portal *portal) -+{ -+ return qm_in(DQRR_VDQCR); -+} -+ -+static inline void qm_dqrr_pdqcr_set(struct qm_portal *portal, u32 pdqcr) -+{ -+ qm_out(DQRR_PDQCR, pdqcr); -+} -+ -+static inline u32 qm_dqrr_pdqcr_get(struct qm_portal *portal) -+{ -+ return qm_in(DQRR_PDQCR); -+} -+ -+static inline u8 qm_dqrr_get_ithresh(struct qm_portal *portal) -+{ -+ register struct qm_dqrr *dqrr = &portal->dqrr; -+ return dqrr->ithresh; -+} -+ -+static inline void qm_dqrr_set_ithresh(struct qm_portal *portal, u8 ithresh) -+{ -+ qm_out(DQRR_ITR, ithresh); -+} -+ -+static inline u8 qm_dqrr_get_maxfill(struct qm_portal *portal) -+{ -+ return (qm_in(CFG) & 0x00f00000) >> 20; -+} -+ -+ -+/* -------------- */ -+/* --- MR API --- */ -+ -+#define MR_CARRYCLEAR(p) \ -+ (void *)((unsigned long)(p) & (~(unsigned long)(QM_MR_SIZE << 6))) -+ -+static inline u8 MR_PTR2IDX(const struct qm_mr_entry *e) -+{ -+ return ((uintptr_t)e >> 6) & (QM_MR_SIZE - 1); -+} -+ -+static inline const struct qm_mr_entry *MR_INC(const struct qm_mr_entry *e) -+{ -+ return MR_CARRYCLEAR(e + 1); -+} -+ -+static inline int qm_mr_init(struct qm_portal *portal, enum qm_mr_pmode pmode, -+ enum qm_mr_cmode cmode) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ u32 cfg; -+ -+ mr->ring = portal->addr.addr_ce + QM_CL_MR; -+ mr->pi = qm_in(MR_PI_CINH) & (QM_MR_SIZE - 1); -+ mr->ci = qm_in(MR_CI_CINH) & (QM_MR_SIZE - 1); -+ mr->cursor = mr->ring + mr->ci; -+ mr->fill = qm_cyc_diff(QM_MR_SIZE, mr->ci, mr->pi); -+ mr->vbit = (qm_in(MR_PI_CINH) & QM_MR_SIZE) ? QM_MR_VERB_VBIT : 0; -+ mr->ithresh = qm_in(MR_ITR); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mr->pmode = pmode; -+ mr->cmode = cmode; -+#endif -+ cfg = (qm_in(CFG) & 0xfffff0ff) | -+ ((cmode & 1) << 8); /* QCSP_CFG:MM */ -+ qm_out(CFG, cfg); -+ return 0; -+} -+ -+static inline void qm_mr_finish(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ if (mr->ci != MR_PTR2IDX(mr->cursor)) -+ pr_crit("Ignoring completed MR entries\n"); -+} -+ -+static inline const struct qm_mr_entry *qm_mr_current(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ if (!mr->fill) -+ return NULL; -+ return mr->cursor; -+} -+ -+static inline u8 qm_mr_cursor(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ return MR_PTR2IDX(mr->cursor); -+} -+ -+static inline u8 qm_mr_next(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ DPA_ASSERT(mr->fill); -+ mr->cursor = MR_INC(mr->cursor); -+ return --mr->fill; -+} -+ -+static inline u8 qm_mr_pci_update(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ u8 diff, old_pi = mr->pi; -+ DPA_ASSERT(mr->pmode == qm_mr_pci); -+ mr->pi = qm_in(MR_PI_CINH); -+ diff = qm_cyc_diff(QM_MR_SIZE, old_pi, mr->pi); -+ mr->fill += diff; -+ return diff; -+} -+ -+static inline void qm_mr_pce_prefetch(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_mr *mr = &portal->mr; -+ DPA_ASSERT(mr->pmode == qm_mr_pce); -+ qm_cl_invalidate(MR_PI); -+ qm_cl_touch_ro(MR_PI); -+} -+ -+static inline u8 qm_mr_pce_update(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ u8 diff, old_pi = mr->pi; -+ DPA_ASSERT(mr->pmode == qm_mr_pce); -+ mr->pi = qm_cl_in(MR_PI) & (QM_MR_SIZE - 1); -+ diff = qm_cyc_diff(QM_MR_SIZE, old_pi, mr->pi); -+ mr->fill += diff; -+ return diff; -+} -+ -+static inline void qm_mr_pvb_update(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ const struct qm_mr_entry *res = qm_cl(mr->ring, mr->pi); -+ DPA_ASSERT(mr->pmode == qm_mr_pvb); -+ /* when accessing 'verb', use __raw_readb() to ensure that compiler -+ * inlining doesn't try to optimise out "excess reads". */ -+ if ((__raw_readb(&res->verb) & QM_MR_VERB_VBIT) == mr->vbit) { -+ mr->pi = (mr->pi + 1) & (QM_MR_SIZE - 1); -+ if (!mr->pi) -+ mr->vbit ^= QM_MR_VERB_VBIT; -+ mr->fill++; -+ res = MR_INC(res); -+ } -+ dcbit_ro(res); -+} -+ -+static inline void qm_mr_cci_consume(struct qm_portal *portal, u8 num) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ DPA_ASSERT(mr->cmode == qm_mr_cci); -+ mr->ci = (mr->ci + num) & (QM_MR_SIZE - 1); -+ qm_out(MR_CI_CINH, mr->ci); -+} -+ -+static inline void qm_mr_cci_consume_to_current(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ DPA_ASSERT(mr->cmode == qm_mr_cci); -+ mr->ci = MR_PTR2IDX(mr->cursor); -+ qm_out(MR_CI_CINH, mr->ci); -+} -+ -+static inline void qm_mr_cce_prefetch(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_mr *mr = &portal->mr; -+ DPA_ASSERT(mr->cmode == qm_mr_cce); -+ qm_cl_invalidate(MR_CI); -+ qm_cl_touch_rw(MR_CI); -+} -+ -+static inline void qm_mr_cce_consume(struct qm_portal *portal, u8 num) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ DPA_ASSERT(mr->cmode == qm_mr_cce); -+ mr->ci = (mr->ci + num) & (QM_MR_SIZE - 1); -+ qm_cl_out(MR_CI, mr->ci); -+} -+ -+static inline void qm_mr_cce_consume_to_current(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ DPA_ASSERT(mr->cmode == qm_mr_cce); -+ mr->ci = MR_PTR2IDX(mr->cursor); -+ qm_cl_out(MR_CI, mr->ci); -+} -+ -+static inline u8 qm_mr_get_ci(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ return mr->ci; -+} -+ -+static inline u8 qm_mr_get_ithresh(struct qm_portal *portal) -+{ -+ register struct qm_mr *mr = &portal->mr; -+ return mr->ithresh; -+} -+ -+static inline void qm_mr_set_ithresh(struct qm_portal *portal, u8 ithresh) -+{ -+ qm_out(MR_ITR, ithresh); -+} -+ -+ -+/* ------------------------------ */ -+/* --- Management command API --- */ -+ -+static inline int qm_mc_init(struct qm_portal *portal) -+{ -+ register struct qm_mc *mc = &portal->mc; -+ mc->cr = portal->addr.addr_ce + QM_CL_CR; -+ mc->rr = portal->addr.addr_ce + QM_CL_RR0; -+ mc->rridx = (__raw_readb(&mc->cr->__dont_write_directly__verb) & -+ QM_MCC_VERB_VBIT) ? 0 : 1; -+ mc->vbit = mc->rridx ? QM_MCC_VERB_VBIT : 0; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = qman_mc_idle; -+#endif -+ return 0; -+} -+ -+static inline void qm_mc_finish(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_mc *mc = &portal->mc; -+ DPA_ASSERT(mc->state == qman_mc_idle); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ if (mc->state != qman_mc_idle) -+ pr_crit("Losing incomplete MC command\n"); -+#endif -+} -+ -+static inline struct qm_mc_command *qm_mc_start(struct qm_portal *portal) -+{ -+ register struct qm_mc *mc = &portal->mc; -+ DPA_ASSERT(mc->state == qman_mc_idle); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = qman_mc_user; -+#endif -+#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) -+ dcbz_64(mc->cr); -+#endif -+ return mc->cr; -+} -+ -+static inline void qm_mc_abort(struct qm_portal *portal) -+{ -+ __maybe_unused register struct qm_mc *mc = &portal->mc; -+ DPA_ASSERT(mc->state == qman_mc_user); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = qman_mc_idle; -+#endif -+} -+ -+static inline void qm_mc_commit(struct qm_portal *portal, u8 myverb) -+{ -+ register struct qm_mc *mc = &portal->mc; -+ struct qm_mc_result *rr = mc->rr + mc->rridx; -+ DPA_ASSERT(mc->state == qman_mc_user); -+ lwsync(); -+ mc->cr->__dont_write_directly__verb = myverb | mc->vbit; -+ dcbf(mc->cr); -+ dcbit_ro(rr); -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = qman_mc_hw; -+#endif -+} -+ -+static inline struct qm_mc_result *qm_mc_result(struct qm_portal *portal) -+{ -+ register struct qm_mc *mc = &portal->mc; -+ struct qm_mc_result *rr = mc->rr + mc->rridx; -+ DPA_ASSERT(mc->state == qman_mc_hw); -+ /* The inactive response register's verb byte always returns zero until -+ * its command is submitted and completed. This includes the valid-bit, -+ * in case you were wondering... */ -+ if (!__raw_readb(&rr->verb)) { -+ dcbit_ro(rr); -+ return NULL; -+ } -+ mc->rridx ^= 1; -+ mc->vbit ^= QM_MCC_VERB_VBIT; -+#ifdef CONFIG_FSL_DPA_CHECKING -+ mc->state = qman_mc_idle; -+#endif -+ return rr; -+} -+ -+ -+/* ------------------------------------- */ -+/* --- Portal interrupt register API --- */ -+ -+static inline int qm_isr_init(__always_unused struct qm_portal *portal) -+{ -+ return 0; -+} -+ -+static inline void qm_isr_finish(__always_unused struct qm_portal *portal) -+{ -+} -+ -+static inline void qm_isr_set_iperiod(struct qm_portal *portal, u16 iperiod) -+{ -+ qm_out(ITPR, iperiod); -+} -+ -+static inline u32 __qm_isr_read(struct qm_portal *portal, enum qm_isr_reg n) -+{ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ return __qm_in(&portal->addr, QM_REG_ISR + (n << 6)); -+#else -+ return __qm_in(&portal->addr, QM_REG_ISR + (n << 2)); -+#endif -+} -+ -+static inline void __qm_isr_write(struct qm_portal *portal, enum qm_isr_reg n, -+ u32 val) -+{ -+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -+ __qm_out(&portal->addr, QM_REG_ISR + (n << 6), val); -+#else -+ __qm_out(&portal->addr, QM_REG_ISR + (n << 2), val); -+#endif -+} -+ -+/* Cleanup FQs */ -+static inline int qm_shutdown_fq(struct qm_portal **portal, int portal_count, -+ u32 fqid) -+{ -+ -+ struct qm_mc_command *mcc; -+ struct qm_mc_result *mcr; -+ u8 state; -+ int orl_empty, fq_empty, i, drain = 0; -+ u32 result; -+ u32 channel, wq; -+ u16 dest_wq; -+ -+ /* Determine the state of the FQID */ -+ mcc = qm_mc_start(portal[0]); -+ mcc->queryfq_np.fqid = cpu_to_be32(fqid); -+ qm_mc_commit(portal[0], QM_MCC_VERB_QUERYFQ_NP); -+ while (!(mcr = qm_mc_result(portal[0]))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP); -+ state = mcr->queryfq_np.state & QM_MCR_NP_STATE_MASK; -+ if (state == QM_MCR_NP_STATE_OOS) -+ return 0; /* Already OOS, no need to do anymore checks */ -+ -+ /* Query which channel the FQ is using */ -+ mcc = qm_mc_start(portal[0]); -+ mcc->queryfq.fqid = cpu_to_be32(fqid); -+ qm_mc_commit(portal[0], QM_MCC_VERB_QUERYFQ); -+ while (!(mcr = qm_mc_result(portal[0]))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ); -+ -+ /* Need to store these since the MCR gets reused */ -+ dest_wq = be16_to_cpu(mcr->queryfq.fqd.dest_wq); -+ wq = dest_wq & 0x7; -+ channel = dest_wq>>3; -+ -+ switch (state) { -+ case QM_MCR_NP_STATE_TEN_SCHED: -+ case QM_MCR_NP_STATE_TRU_SCHED: -+ case QM_MCR_NP_STATE_ACTIVE: -+ case QM_MCR_NP_STATE_PARKED: -+ orl_empty = 0; -+ mcc = qm_mc_start(portal[0]); -+ mcc->alterfq.fqid = cpu_to_be32(fqid); -+ qm_mc_commit(portal[0], QM_MCC_VERB_ALTER_RETIRE); -+ while (!(mcr = qm_mc_result(portal[0]))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_MCR_VERB_ALTER_RETIRE); -+ result = mcr->result; /* Make a copy as we reuse MCR below */ -+ -+ if (result == QM_MCR_RESULT_PENDING) { -+ /* Need to wait for the FQRN in the message ring, which -+ will only occur once the FQ has been drained. In -+ order for the FQ to drain the portal needs to be set -+ to dequeue from the channel the FQ is scheduled on */ -+ const struct qm_mr_entry *msg; -+ const struct qm_dqrr_entry *dqrr = NULL; -+ int found_fqrn = 0; -+ u16 dequeue_wq = 0; -+ -+ /* Flag that we need to drain FQ */ -+ drain = 1; -+ -+ if (channel >= qm_channel_pool1 && -+ channel < (qm_channel_pool1 + 15)) { -+ /* Pool channel, enable the bit in the portal */ -+ dequeue_wq = (channel - -+ qm_channel_pool1 + 1)<<4 | wq; -+ } else if (channel < qm_channel_pool1) { -+ /* Dedicated channel */ -+ dequeue_wq = wq; -+ } else { -+ pr_info("Cannot recover FQ 0x%x, it is " -+ "scheduled on channel 0x%x", -+ fqid, channel); -+ return -EBUSY; -+ } -+ /* Set the sdqcr to drain this channel */ -+ if (channel < qm_channel_pool1) -+ for (i = 0; i < portal_count; i++) -+ qm_dqrr_sdqcr_set(portal[i], -+ QM_SDQCR_TYPE_ACTIVE | -+ QM_SDQCR_CHANNELS_DEDICATED); -+ else -+ for (i = 0; i < portal_count; i++) -+ qm_dqrr_sdqcr_set( -+ portal[i], -+ QM_SDQCR_TYPE_ACTIVE | -+ QM_SDQCR_CHANNELS_POOL_CONV -+ (channel)); -+ while (!found_fqrn) { -+ /* Keep draining DQRR while checking the MR*/ -+ for (i = 0; i < portal_count; i++) { -+ qm_dqrr_pvb_update(portal[i]); -+ dqrr = qm_dqrr_current(portal[i]); -+ while (dqrr) { -+ qm_dqrr_cdc_consume_1ptr( -+ portal[i], dqrr, 0); -+ qm_dqrr_pvb_update(portal[i]); -+ qm_dqrr_next(portal[i]); -+ dqrr = qm_dqrr_current( -+ portal[i]); -+ } -+ /* Process message ring too */ -+ qm_mr_pvb_update(portal[i]); -+ msg = qm_mr_current(portal[i]); -+ while (msg) { -+ if ((msg->verb & -+ QM_MR_VERB_TYPE_MASK) -+ == QM_MR_VERB_FQRN) -+ found_fqrn = 1; -+ qm_mr_next(portal[i]); -+ qm_mr_cci_consume_to_current( -+ portal[i]); -+ qm_mr_pvb_update(portal[i]); -+ msg = qm_mr_current(portal[i]); -+ } -+ cpu_relax(); -+ } -+ } -+ } -+ if (result != QM_MCR_RESULT_OK && -+ result != QM_MCR_RESULT_PENDING) { -+ /* error */ -+ pr_err("qman_retire_fq failed on FQ 0x%x, result=0x%x\n", -+ fqid, result); -+ return -1; -+ } -+ if (!(mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT)) { -+ /* ORL had no entries, no need to wait until the -+ ERNs come in */ -+ orl_empty = 1; -+ } -+ /* Retirement succeeded, check to see if FQ needs -+ to be drained */ -+ if (drain || mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY) { -+ /* FQ is Not Empty, drain using volatile DQ commands */ -+ fq_empty = 0; -+ do { -+ const struct qm_dqrr_entry *dqrr = NULL; -+ u32 vdqcr = fqid | QM_VDQCR_NUMFRAMES_SET(3); -+ qm_dqrr_vdqcr_set(portal[0], vdqcr); -+ -+ /* Wait for a dequeue to occur */ -+ while (dqrr == NULL) { -+ qm_dqrr_pvb_update(portal[0]); -+ dqrr = qm_dqrr_current(portal[0]); -+ if (!dqrr) -+ cpu_relax(); -+ } -+ /* Process the dequeues, making sure to -+ empty the ring completely */ -+ while (dqrr) { -+ if (be32_to_cpu(dqrr->fqid) == fqid && -+ dqrr->stat & QM_DQRR_STAT_FQ_EMPTY) -+ fq_empty = 1; -+ qm_dqrr_cdc_consume_1ptr(portal[0], -+ dqrr, 0); -+ qm_dqrr_pvb_update(portal[0]); -+ qm_dqrr_next(portal[0]); -+ dqrr = qm_dqrr_current(portal[0]); -+ } -+ } while (fq_empty == 0); -+ } -+ for (i = 0; i < portal_count; i++) -+ qm_dqrr_sdqcr_set(portal[i], 0); -+ -+ /* Wait for the ORL to have been completely drained */ -+ while (orl_empty == 0) { -+ const struct qm_mr_entry *msg; -+ qm_mr_pvb_update(portal[0]); -+ msg = qm_mr_current(portal[0]); -+ while (msg) { -+ if ((msg->verb & QM_MR_VERB_TYPE_MASK) == -+ QM_MR_VERB_FQRL) -+ orl_empty = 1; -+ qm_mr_next(portal[0]); -+ qm_mr_cci_consume_to_current(portal[0]); -+ qm_mr_pvb_update(portal[0]); -+ msg = qm_mr_current(portal[0]); -+ } -+ cpu_relax(); -+ } -+ mcc = qm_mc_start(portal[0]); -+ mcc->alterfq.fqid = cpu_to_be32(fqid); -+ qm_mc_commit(portal[0], QM_MCC_VERB_ALTER_OOS); -+ while (!(mcr = qm_mc_result(portal[0]))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_MCR_VERB_ALTER_OOS); -+ if (mcr->result != QM_MCR_RESULT_OK) { -+ pr_err("OOS after drain Failed on FQID 0x%x, result 0x%x\n", -+ fqid, mcr->result); -+ return -1; -+ } -+ return 0; -+ case QM_MCR_NP_STATE_RETIRED: -+ /* Send OOS Command */ -+ mcc = qm_mc_start(portal[0]); -+ mcc->alterfq.fqid = cpu_to_be32(fqid); -+ qm_mc_commit(portal[0], QM_MCC_VERB_ALTER_OOS); -+ while (!(mcr = qm_mc_result(portal[0]))) -+ cpu_relax(); -+ DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == -+ QM_MCR_VERB_ALTER_OOS); -+ if (mcr->result) { -+ pr_err("OOS Failed on FQID 0x%x\n", fqid); -+ return -1; -+ } -+ return 0; -+ } -+ return -1; -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_private.h -@@ -0,0 +1,398 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "dpa_sys.h" -+#include <linux/fsl_qman.h> -+#include <linux/iommu.h> -+ -+#if defined(CONFIG_FSL_PAMU) -+#include <asm/fsl_pamu_stash.h> -+#endif -+ -+#if !defined(CONFIG_FSL_QMAN_FQ_LOOKUP) && defined(CONFIG_PPC64) -+#error "_PPC64 requires _FSL_QMAN_FQ_LOOKUP" -+#endif -+ -+#define QBMAN_ANY_PORTAL_IDX 0xffffffff -+ /* ----------------- */ -+ /* Congestion Groups */ -+ /* ----------------- */ -+/* This wrapper represents a bit-array for the state of the 256 Qman congestion -+ * groups. Is also used as a *mask* for congestion groups, eg. so we ignore -+ * those that don't concern us. We harness the structure and accessor details -+ * already used in the management command to query congestion groups. */ -+struct qman_cgrs { -+ struct __qm_mcr_querycongestion q; -+}; -+static inline void qman_cgrs_init(struct qman_cgrs *c) -+{ -+ memset(c, 0, sizeof(*c)); -+} -+static inline void qman_cgrs_fill(struct qman_cgrs *c) -+{ -+ memset(c, 0xff, sizeof(*c)); -+} -+static inline int qman_cgrs_get(struct qman_cgrs *c, int num) -+{ -+ return QM_MCR_QUERYCONGESTION(&c->q, num); -+} -+static inline void qman_cgrs_set(struct qman_cgrs *c, int num) -+{ -+ c->q.__state[__CGR_WORD(num)] |= (0x80000000 >> __CGR_SHIFT(num)); -+} -+static inline void qman_cgrs_unset(struct qman_cgrs *c, int num) -+{ -+ c->q.__state[__CGR_WORD(num)] &= ~(0x80000000 >> __CGR_SHIFT(num)); -+} -+static inline int qman_cgrs_next(struct qman_cgrs *c, int num) -+{ -+ while ((++num < __CGR_NUM) && !qman_cgrs_get(c, num)) -+ ; -+ return num; -+} -+static inline void qman_cgrs_cp(struct qman_cgrs *dest, -+ const struct qman_cgrs *src) -+{ -+ *dest = *src; -+} -+static inline void qman_cgrs_and(struct qman_cgrs *dest, -+ const struct qman_cgrs *a, const struct qman_cgrs *b) -+{ -+ int ret; -+ u32 *_d = dest->q.__state; -+ const u32 *_a = a->q.__state; -+ const u32 *_b = b->q.__state; -+ for (ret = 0; ret < 8; ret++) -+ *(_d++) = *(_a++) & *(_b++); -+} -+static inline void qman_cgrs_xor(struct qman_cgrs *dest, -+ const struct qman_cgrs *a, const struct qman_cgrs *b) -+{ -+ int ret; -+ u32 *_d = dest->q.__state; -+ const u32 *_a = a->q.__state; -+ const u32 *_b = b->q.__state; -+ for (ret = 0; ret < 8; ret++) -+ *(_d++) = *(_a++) ^ *(_b++); -+} -+ -+ /* ----------------------- */ -+ /* CEETM Congestion Groups */ -+ /* ----------------------- */ -+/* This wrapper represents a bit-array for the state of the 512 Qman CEETM -+ * congestion groups. -+ */ -+struct qman_ccgrs { -+ struct __qm_mcr_querycongestion q[2]; -+}; -+static inline void qman_ccgrs_init(struct qman_ccgrs *c) -+{ -+ memset(c, 0, sizeof(*c)); -+} -+static inline void qman_ccgrs_fill(struct qman_ccgrs *c) -+{ -+ memset(c, 0xff, sizeof(*c)); -+} -+static inline int qman_ccgrs_get(struct qman_ccgrs *c, int num) -+{ -+ if (num < __CGR_NUM) -+ return QM_MCR_QUERYCONGESTION(&c->q[0], num); -+ else -+ return QM_MCR_QUERYCONGESTION(&c->q[1], (num - __CGR_NUM)); -+} -+static inline int qman_ccgrs_next(struct qman_ccgrs *c, int num) -+{ -+ while ((++num < __CGR_NUM) && !qman_ccgrs_get(c, num)) -+ ; -+ return num; -+} -+static inline void qman_ccgrs_cp(struct qman_ccgrs *dest, -+ const struct qman_ccgrs *src) -+{ -+ *dest = *src; -+} -+static inline void qman_ccgrs_and(struct qman_ccgrs *dest, -+ const struct qman_ccgrs *a, const struct qman_ccgrs *b) -+{ -+ int ret, i; -+ u32 *_d; -+ const u32 *_a, *_b; -+ for (i = 0; i < 2; i++) { -+ _d = dest->q[i].__state; -+ _a = a->q[i].__state; -+ _b = b->q[i].__state; -+ for (ret = 0; ret < 8; ret++) -+ *(_d++) = *(_a++) & *(_b++); -+ } -+} -+static inline void qman_ccgrs_xor(struct qman_ccgrs *dest, -+ const struct qman_ccgrs *a, const struct qman_ccgrs *b) -+{ -+ int ret, i; -+ u32 *_d; -+ const u32 *_a, *_b; -+ for (i = 0; i < 2; i++) { -+ _d = dest->q[i].__state; -+ _a = a->q[i].__state; -+ _b = b->q[i].__state; -+ for (ret = 0; ret < 8; ret++) -+ *(_d++) = *(_a++) ^ *(_b++); -+ } -+} -+ -+/* used by CCSR and portal interrupt code */ -+enum qm_isr_reg { -+ qm_isr_status = 0, -+ qm_isr_enable = 1, -+ qm_isr_disable = 2, -+ qm_isr_inhibit = 3 -+}; -+ -+struct qm_portal_config { -+ /* Corenet portal addresses; -+ * [0]==cache-enabled, [1]==cache-inhibited. */ -+ __iomem void *addr_virt[2]; -+ struct resource addr_phys[2]; -+ struct device dev; -+ struct iommu_domain *iommu_domain; -+ /* Allow these to be joined in lists */ -+ struct list_head list; -+ /* User-visible portal configuration settings */ -+ struct qman_portal_config public_cfg; -+ /* power management saved data */ -+ u32 saved_isdr; -+}; -+ -+/* Revision info (for errata and feature handling) */ -+#define QMAN_REV11 0x0101 -+#define QMAN_REV12 0x0102 -+#define QMAN_REV20 0x0200 -+#define QMAN_REV30 0x0300 -+#define QMAN_REV31 0x0301 -+#define QMAN_REV32 0x0302 -+ -+/* QMan REV_2 register contains the Cfg option */ -+#define QMAN_REV_CFG_0 0x0 -+#define QMAN_REV_CFG_1 0x1 -+#define QMAN_REV_CFG_2 0x2 -+#define QMAN_REV_CFG_3 0x3 -+ -+extern u16 qman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */ -+extern u8 qman_ip_cfg; -+extern u32 qman_clk; -+extern u16 qman_portal_max; -+ -+#ifdef CONFIG_FSL_QMAN_CONFIG -+/* Hooks from qman_driver.c to qman_config.c */ -+int qman_init_ccsr(struct device_node *node); -+void qman_liodn_fixup(u16 channel); -+int qman_set_sdest(u16 channel, unsigned int cpu_idx); -+size_t get_qman_fqd_size(void); -+#else -+static inline size_t get_qman_fqd_size(void) -+{ -+ return (PAGE_SIZE << CONFIG_FSL_QMAN_FQD_SZ); -+} -+#endif -+ -+int qm_set_wpm(int wpm); -+int qm_get_wpm(int *wpm); -+ -+/* Hooks from qman_driver.c in to qman_high.c */ -+struct qman_portal *qman_create_portal( -+ struct qman_portal *portal, -+ const struct qm_portal_config *config, -+ const struct qman_cgrs *cgrs); -+ -+struct qman_portal *qman_create_affine_portal( -+ const struct qm_portal_config *config, -+ const struct qman_cgrs *cgrs); -+struct qman_portal *qman_create_affine_slave(struct qman_portal *redirect, -+ int cpu); -+const struct qm_portal_config *qman_destroy_affine_portal(void); -+void qman_destroy_portal(struct qman_portal *qm); -+ -+/* Hooks from fsl_usdpaa.c to qman_driver.c */ -+struct qm_portal_config *qm_get_unused_portal(void); -+struct qm_portal_config *qm_get_unused_portal_idx(uint32_t idx); -+ -+void qm_put_unused_portal(struct qm_portal_config *pcfg); -+void qm_set_liodns(struct qm_portal_config *pcfg); -+ -+/* This CGR feature is supported by h/w and required by unit-tests and the -+ * debugfs hooks, so is implemented in the driver. However it allows an explicit -+ * corruption of h/w fields by s/w that are usually incorruptible (because the -+ * counters are usually maintained entirely within h/w). As such, we declare -+ * this API internally. */ -+int qman_testwrite_cgr(struct qman_cgr *cgr, u64 i_bcnt, -+ struct qm_mcr_cgrtestwrite *result); -+ -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+/* If the fq object pointer is greater than the size of context_b field, -+ * than a lookup table is required. */ -+int qman_setup_fq_lookup_table(size_t num_entries); -+#endif -+ -+ -+/*************************************************/ -+/* QMan s/w corenet portal, low-level i/face */ -+/*************************************************/ -+ -+/* Note: most functions are only used by the high-level interface, so are -+ * inlined from qman_low.h. The stuff below is for use by other parts of the -+ * driver. */ -+ -+/* For qm_dqrr_sdqcr_set(); Choose one SOURCE. Choose one COUNT. Choose one -+ * dequeue TYPE. Choose TOKEN (8-bit). -+ * If SOURCE == CHANNELS, -+ * Choose CHANNELS_DEDICATED and/or CHANNELS_POOL(n). -+ * You can choose DEDICATED_PRECEDENCE if the portal channel should have -+ * priority. -+ * If SOURCE == SPECIFICWQ, -+ * Either select the work-queue ID with SPECIFICWQ_WQ(), or select the -+ * channel (SPECIFICWQ_DEDICATED or SPECIFICWQ_POOL()) and specify the -+ * work-queue priority (0-7) with SPECIFICWQ_WQ() - either way, you get the -+ * same value. -+ */ -+#define QM_SDQCR_SOURCE_CHANNELS 0x0 -+#define QM_SDQCR_SOURCE_SPECIFICWQ 0x40000000 -+#define QM_SDQCR_COUNT_EXACT1 0x0 -+#define QM_SDQCR_COUNT_UPTO3 0x20000000 -+#define QM_SDQCR_DEDICATED_PRECEDENCE 0x10000000 -+#define QM_SDQCR_TYPE_MASK 0x03000000 -+#define QM_SDQCR_TYPE_NULL 0x0 -+#define QM_SDQCR_TYPE_PRIO_QOS 0x01000000 -+#define QM_SDQCR_TYPE_ACTIVE_QOS 0x02000000 -+#define QM_SDQCR_TYPE_ACTIVE 0x03000000 -+#define QM_SDQCR_TOKEN_MASK 0x00ff0000 -+#define QM_SDQCR_TOKEN_SET(v) (((v) & 0xff) << 16) -+#define QM_SDQCR_TOKEN_GET(v) (((v) >> 16) & 0xff) -+#define QM_SDQCR_CHANNELS_DEDICATED 0x00008000 -+#define QM_SDQCR_SPECIFICWQ_MASK 0x000000f7 -+#define QM_SDQCR_SPECIFICWQ_DEDICATED 0x00000000 -+#define QM_SDQCR_SPECIFICWQ_POOL(n) ((n) << 4) -+#define QM_SDQCR_SPECIFICWQ_WQ(n) (n) -+ -+/* For qm_dqrr_vdqcr_set(): use FQID(n) to fill in the frame queue ID */ -+#define QM_VDQCR_FQID_MASK 0x00ffffff -+#define QM_VDQCR_FQID(n) ((n) & QM_VDQCR_FQID_MASK) -+ -+/* For qm_dqrr_pdqcr_set(); Choose one MODE. Choose one COUNT. -+ * If MODE==SCHEDULED -+ * Choose SCHEDULED_CHANNELS or SCHEDULED_SPECIFICWQ. Choose one dequeue TYPE. -+ * If CHANNELS, -+ * Choose CHANNELS_DEDICATED and/or CHANNELS_POOL() channels. -+ * You can choose DEDICATED_PRECEDENCE if the portal channel should have -+ * priority. -+ * If SPECIFICWQ, -+ * Either select the work-queue ID with SPECIFICWQ_WQ(), or select the -+ * channel (SPECIFICWQ_DEDICATED or SPECIFICWQ_POOL()) and specify the -+ * work-queue priority (0-7) with SPECIFICWQ_WQ() - either way, you get the -+ * same value. -+ * If MODE==UNSCHEDULED -+ * Choose FQID(). -+ */ -+#define QM_PDQCR_MODE_SCHEDULED 0x0 -+#define QM_PDQCR_MODE_UNSCHEDULED 0x80000000 -+#define QM_PDQCR_SCHEDULED_CHANNELS 0x0 -+#define QM_PDQCR_SCHEDULED_SPECIFICWQ 0x40000000 -+#define QM_PDQCR_COUNT_EXACT1 0x0 -+#define QM_PDQCR_COUNT_UPTO3 0x20000000 -+#define QM_PDQCR_DEDICATED_PRECEDENCE 0x10000000 -+#define QM_PDQCR_TYPE_MASK 0x03000000 -+#define QM_PDQCR_TYPE_NULL 0x0 -+#define QM_PDQCR_TYPE_PRIO_QOS 0x01000000 -+#define QM_PDQCR_TYPE_ACTIVE_QOS 0x02000000 -+#define QM_PDQCR_TYPE_ACTIVE 0x03000000 -+#define QM_PDQCR_CHANNELS_DEDICATED 0x00008000 -+#define QM_PDQCR_CHANNELS_POOL(n) (0x00008000 >> (n)) -+#define QM_PDQCR_SPECIFICWQ_MASK 0x000000f7 -+#define QM_PDQCR_SPECIFICWQ_DEDICATED 0x00000000 -+#define QM_PDQCR_SPECIFICWQ_POOL(n) ((n) << 4) -+#define QM_PDQCR_SPECIFICWQ_WQ(n) (n) -+#define QM_PDQCR_FQID(n) ((n) & 0xffffff) -+ -+/* Used by all portal interrupt registers except 'inhibit' -+ * Channels with frame availability -+ */ -+#define QM_PIRQ_DQAVAIL 0x0000ffff -+ -+/* The DQAVAIL interrupt fields break down into these bits; */ -+#define QM_DQAVAIL_PORTAL 0x8000 /* Portal channel */ -+#define QM_DQAVAIL_POOL(n) (0x8000 >> (n)) /* Pool channel, n==[1..15] */ -+#define QM_DQAVAIL_MASK 0xffff -+/* This mask contains all the "irqsource" bits visible to API users */ -+#define QM_PIRQ_VISIBLE (QM_PIRQ_SLOW | QM_PIRQ_DQRI) -+ -+/* These are qm_<reg>_<verb>(). So for example, qm_disable_write() means "write -+ * the disable register" rather than "disable the ability to write". */ -+#define qm_isr_status_read(qm) __qm_isr_read(qm, qm_isr_status) -+#define qm_isr_status_clear(qm, m) __qm_isr_write(qm, qm_isr_status, m) -+#define qm_isr_enable_read(qm) __qm_isr_read(qm, qm_isr_enable) -+#define qm_isr_enable_write(qm, v) __qm_isr_write(qm, qm_isr_enable, v) -+#define qm_isr_disable_read(qm) __qm_isr_read(qm, qm_isr_disable) -+#define qm_isr_disable_write(qm, v) __qm_isr_write(qm, qm_isr_disable, v) -+/* TODO: unfortunate name-clash here, reword? */ -+#define qm_isr_inhibit(qm) __qm_isr_write(qm, qm_isr_inhibit, 1) -+#define qm_isr_uninhibit(qm) __qm_isr_write(qm, qm_isr_inhibit, 0) -+ -+#ifdef CONFIG_FSL_QMAN_CONFIG -+int qman_have_ccsr(void); -+#else -+#define qman_have_ccsr 0 -+#endif -+ -+__init int qman_init(void); -+__init int qman_resource_init(void); -+ -+/* CEETM related */ -+#define QMAN_CEETM_MAX 2 -+extern u8 num_ceetms; -+extern struct qm_ceetm qman_ceetms[QMAN_CEETM_MAX]; -+int qman_sp_enable_ceetm_mode(enum qm_dc_portal portal, u16 sub_portal); -+int qman_sp_disable_ceetm_mode(enum qm_dc_portal portal, u16 sub_portal); -+int qman_ceetm_set_prescaler(enum qm_dc_portal portal); -+int qman_ceetm_get_prescaler(u16 *pres); -+int qman_ceetm_query_cq(unsigned int cqid, unsigned int dcpid, -+ struct qm_mcr_ceetm_cq_query *cq_query); -+int qman_ceetm_query_ccgr(struct qm_mcc_ceetm_ccgr_query *ccgr_query, -+ struct qm_mcr_ceetm_ccgr_query *response); -+int qman_ceetm_get_xsfdr(enum qm_dc_portal portal, unsigned int *num); -+ -+extern void *affine_portals[NR_CPUS]; -+const struct qm_portal_config *qman_get_qm_portal_config( -+ struct qman_portal *portal); -+ -+/* power management */ -+#ifdef CONFIG_SUSPEND -+void suspend_unused_qportal(void); -+void resume_unused_qportal(void); -+#endif ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_test.c -@@ -0,0 +1,57 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "qman_test.h" -+ -+MODULE_AUTHOR("Geoff Thorpe"); -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_DESCRIPTION("Qman testing"); -+ -+static int test_init(void) -+{ -+ int loop = 1; -+ while (loop--) { -+#ifdef CONFIG_FSL_QMAN_TEST_STASH_POTATO -+ qman_test_hotpotato(); -+#endif -+#ifdef CONFIG_FSL_QMAN_TEST_HIGH -+ qman_test_high(); -+#endif -+ } -+ return 0; -+} -+ -+static void test_exit(void) -+{ -+} -+ -+module_init(test_init); -+module_exit(test_exit); ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_test.h -@@ -0,0 +1,45 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/slab.h> -+#include <linux/module.h> -+#include <linux/interrupt.h> -+#include <linux/delay.h> -+#include <linux/sched.h> -+ -+#include <linux/fsl_qman.h> -+ -+void qman_test_hotpotato(void); -+void qman_test_high(void); -+ ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_test_high.c -@@ -0,0 +1,216 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "qman_test.h" -+ -+/*************/ -+/* constants */ -+/*************/ -+ -+#define CGR_ID 27 -+#define POOL_ID 2 -+#define FQ_FLAGS QMAN_FQ_FLAG_DYNAMIC_FQID -+#define NUM_ENQUEUES 10 -+#define NUM_PARTIAL 4 -+#define PORTAL_SDQCR (QM_SDQCR_SOURCE_CHANNELS | \ -+ QM_SDQCR_TYPE_PRIO_QOS | \ -+ QM_SDQCR_TOKEN_SET(0x98) | \ -+ QM_SDQCR_CHANNELS_DEDICATED | \ -+ QM_SDQCR_CHANNELS_POOL(POOL_ID)) -+#define PORTAL_OPAQUE ((void *)0xf00dbeef) -+#define VDQCR_FLAGS (QMAN_VOLATILE_FLAG_WAIT | QMAN_VOLATILE_FLAG_FINISH) -+ -+/*************************************/ -+/* Predeclarations (eg. for fq_base) */ -+/*************************************/ -+ -+static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *, -+ struct qman_fq *, -+ const struct qm_dqrr_entry *); -+static void cb_ern(struct qman_portal *, struct qman_fq *, -+ const struct qm_mr_entry *); -+static void cb_fqs(struct qman_portal *, struct qman_fq *, -+ const struct qm_mr_entry *); -+ -+/***************/ -+/* global vars */ -+/***************/ -+ -+static struct qm_fd fd, fd_dq; -+static struct qman_fq fq_base = { -+ .cb.dqrr = cb_dqrr, -+ .cb.ern = cb_ern, -+ .cb.fqs = cb_fqs -+}; -+static DECLARE_WAIT_QUEUE_HEAD(waitqueue); -+static int retire_complete, sdqcr_complete; -+ -+/**********************/ -+/* internal functions */ -+/**********************/ -+ -+/* Helpers for initialising and "incrementing" a frame descriptor */ -+static void fd_init(struct qm_fd *__fd) -+{ -+ qm_fd_addr_set64(__fd, 0xabdeadbeefLLU); -+ __fd->format = qm_fd_contig_big; -+ __fd->length29 = 0x0000ffff; -+ __fd->cmd = 0xfeedf00d; -+} -+ -+static void fd_inc(struct qm_fd *__fd) -+{ -+ u64 t = qm_fd_addr_get64(__fd); -+ int z = t >> 40; -+ t <<= 1; -+ if (z) -+ t |= 1; -+ qm_fd_addr_set64(__fd, t); -+ __fd->length29--; -+ __fd->cmd++; -+} -+ -+/* The only part of the 'fd' we can't memcmp() is the ppid */ -+static int fd_cmp(const struct qm_fd *a, const struct qm_fd *b) -+{ -+ int r = (qm_fd_addr_get64(a) == qm_fd_addr_get64(b)) ? 0 : -1; -+ if (!r) -+ r = a->format - b->format; -+ if (!r) -+ r = a->opaque - b->opaque; -+ if (!r) -+ r = a->cmd - b->cmd; -+ return r; -+} -+ -+/********/ -+/* test */ -+/********/ -+ -+static void do_enqueues(struct qman_fq *fq) -+{ -+ unsigned int loop; -+ for (loop = 0; loop < NUM_ENQUEUES; loop++) { -+ if (qman_enqueue(fq, &fd, QMAN_ENQUEUE_FLAG_WAIT | -+ (((loop + 1) == NUM_ENQUEUES) ? -+ QMAN_ENQUEUE_FLAG_WAIT_SYNC : 0))) -+ panic("qman_enqueue() failed\n"); -+ fd_inc(&fd); -+ } -+} -+ -+void qman_test_high(void) -+{ -+ unsigned int flags; -+ int res; -+ struct qman_fq *fq = &fq_base; -+ -+ pr_info("qman_test_high starting\n"); -+ fd_init(&fd); -+ fd_init(&fd_dq); -+ -+ /* Initialise (parked) FQ */ -+ if (qman_create_fq(0, FQ_FLAGS, fq)) -+ panic("qman_create_fq() failed\n"); -+ if (qman_init_fq(fq, QMAN_INITFQ_FLAG_LOCAL, NULL)) -+ panic("qman_init_fq() failed\n"); -+ -+ /* Do enqueues + VDQCR, twice. (Parked FQ) */ -+ do_enqueues(fq); -+ pr_info("VDQCR (till-empty);\n"); -+ if (qman_volatile_dequeue(fq, VDQCR_FLAGS, -+ QM_VDQCR_NUMFRAMES_TILLEMPTY)) -+ panic("qman_volatile_dequeue() failed\n"); -+ do_enqueues(fq); -+ pr_info("VDQCR (%d of %d);\n", NUM_PARTIAL, NUM_ENQUEUES); -+ if (qman_volatile_dequeue(fq, VDQCR_FLAGS, -+ QM_VDQCR_NUMFRAMES_SET(NUM_PARTIAL))) -+ panic("qman_volatile_dequeue() failed\n"); -+ pr_info("VDQCR (%d of %d);\n", NUM_ENQUEUES - NUM_PARTIAL, -+ NUM_ENQUEUES); -+ if (qman_volatile_dequeue(fq, VDQCR_FLAGS, -+ QM_VDQCR_NUMFRAMES_SET(NUM_ENQUEUES - NUM_PARTIAL))) -+ panic("qman_volatile_dequeue() failed\n"); -+ -+ do_enqueues(fq); -+ pr_info("scheduled dequeue (till-empty)\n"); -+ if (qman_schedule_fq(fq)) -+ panic("qman_schedule_fq() failed\n"); -+ wait_event(waitqueue, sdqcr_complete); -+ -+ /* Retire and OOS the FQ */ -+ res = qman_retire_fq(fq, &flags); -+ if (res < 0) -+ panic("qman_retire_fq() failed\n"); -+ wait_event(waitqueue, retire_complete); -+ if (flags & QMAN_FQ_STATE_BLOCKOOS) -+ panic("leaking frames\n"); -+ if (qman_oos_fq(fq)) -+ panic("qman_oos_fq() failed\n"); -+ qman_destroy_fq(fq, 0); -+ pr_info("qman_test_high finished\n"); -+} -+ -+static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *p, -+ struct qman_fq *fq, -+ const struct qm_dqrr_entry *dq) -+{ -+ if (fd_cmp(&fd_dq, &dq->fd)) { -+ pr_err("BADNESS: dequeued frame doesn't match;\n"); -+ pr_err("Expected 0x%llx, got 0x%llx\n", -+ (unsigned long long)fd_dq.length29, -+ (unsigned long long)dq->fd.length29); -+ BUG(); -+ } -+ fd_inc(&fd_dq); -+ if (!(dq->stat & QM_DQRR_STAT_UNSCHEDULED) && !fd_cmp(&fd_dq, &fd)) { -+ sdqcr_complete = 1; -+ wake_up(&waitqueue); -+ } -+ return qman_cb_dqrr_consume; -+} -+ -+static void cb_ern(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_mr_entry *msg) -+{ -+ panic("cb_ern() unimplemented"); -+} -+ -+static void cb_fqs(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_mr_entry *msg) -+{ -+ u8 verb = (msg->verb & QM_MR_VERB_TYPE_MASK); -+ if ((verb != QM_MR_VERB_FQRN) && (verb != QM_MR_VERB_FQRNI)) -+ panic("unexpected FQS message"); -+ pr_info("Retirement message received\n"); -+ retire_complete = 1; -+ wake_up(&waitqueue); -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_test_hotpotato.c -@@ -0,0 +1,502 @@ -+/* Copyright 2009-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include <linux/kthread.h> -+#include <linux/platform_device.h> -+#include <linux/dma-mapping.h> -+#include "qman_test.h" -+ -+/* Algorithm: -+ * -+ * Each cpu will have HP_PER_CPU "handlers" set up, each of which incorporates -+ * an rx/tx pair of FQ objects (both of which are stashed on dequeue). The -+ * organisation of FQIDs is such that the HP_PER_CPU*NUM_CPUS handlers will -+ * shuttle a "hot potato" frame around them such that every forwarding action -+ * moves it from one cpu to another. (The use of more than one handler per cpu -+ * is to allow enough handlers/FQs to truly test the significance of caching - -+ * ie. when cache-expiries are occurring.) -+ * -+ * The "hot potato" frame content will be HP_NUM_WORDS*4 bytes in size, and the -+ * first and last words of the frame data will undergo a transformation step on -+ * each forwarding action. To achieve this, each handler will be assigned a -+ * 32-bit "mixer", that is produced using a 32-bit LFSR. When a frame is -+ * received by a handler, the mixer of the expected sender is XOR'd into all -+ * words of the entire frame, which is then validated against the original -+ * values. Then, before forwarding, the entire frame is XOR'd with the mixer of -+ * the current handler. Apart from validating that the frame is taking the -+ * expected path, this also provides some quasi-realistic overheads to each -+ * forwarding action - dereferencing *all* the frame data, computation, and -+ * conditional branching. There is a "special" handler designated to act as the -+ * instigator of the test by creating an enqueuing the "hot potato" frame, and -+ * to determine when the test has completed by counting HP_LOOPS iterations. -+ * -+ * Init phases: -+ * -+ * 1. prepare each cpu's 'hp_cpu' struct using on_each_cpu(,,1) and link them -+ * into 'hp_cpu_list'. Specifically, set processor_id, allocate HP_PER_CPU -+ * handlers and link-list them (but do no other handler setup). -+ * -+ * 2. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each -+ * hp_cpu's 'iterator' to point to its first handler. With each loop, -+ * allocate rx/tx FQIDs and mixer values to the hp_cpu's iterator handler -+ * and advance the iterator for the next loop. This includes a final fixup, -+ * which connects the last handler to the first (and which is why phase 2 -+ * and 3 are separate). -+ * -+ * 3. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each -+ * hp_cpu's 'iterator' to point to its first handler. With each loop, -+ * initialise FQ objects and advance the iterator for the next loop. -+ * Moreover, do this initialisation on the cpu it applies to so that Rx FQ -+ * initialisation targets the correct cpu. -+ */ -+ -+/* helper to run something on all cpus (can't use on_each_cpu(), as that invokes -+ * the fn from irq context, which is too restrictive). */ -+struct bstrap { -+ void (*fn)(void); -+ atomic_t started; -+}; -+static int bstrap_fn(void *__bstrap) -+{ -+ struct bstrap *bstrap = __bstrap; -+ atomic_inc(&bstrap->started); -+ bstrap->fn(); -+ while (!kthread_should_stop()) -+ msleep(1); -+ return 0; -+} -+static int on_all_cpus(void (*fn)(void)) -+{ -+ int cpu; -+ for_each_cpu(cpu, cpu_online_mask) { -+ struct bstrap bstrap = { -+ .fn = fn, -+ .started = ATOMIC_INIT(0) -+ }; -+ struct task_struct *k = kthread_create(bstrap_fn, &bstrap, -+ "hotpotato%d", cpu); -+ int ret; -+ if (IS_ERR(k)) -+ return -ENOMEM; -+ kthread_bind(k, cpu); -+ wake_up_process(k); -+ /* If we call kthread_stop() before the "wake up" has had an -+ * effect, then the thread may exit with -EINTR without ever -+ * running the function. So poll until it's started before -+ * requesting it to stop. */ -+ while (!atomic_read(&bstrap.started)) -+ msleep(10); -+ ret = kthread_stop(k); -+ if (ret) -+ return ret; -+ } -+ return 0; -+} -+ -+struct hp_handler { -+ -+ /* The following data is stashed when 'rx' is dequeued; */ -+ /* -------------- */ -+ /* The Rx FQ, dequeues of which will stash the entire hp_handler */ -+ struct qman_fq rx; -+ /* The Tx FQ we should forward to */ -+ struct qman_fq tx; -+ /* The value we XOR post-dequeue, prior to validating */ -+ u32 rx_mixer; -+ /* The value we XOR pre-enqueue, after validating */ -+ u32 tx_mixer; -+ /* what the hotpotato address should be on dequeue */ -+ dma_addr_t addr; -+ u32 *frame_ptr; -+ -+ /* The following data isn't (necessarily) stashed on dequeue; */ -+ /* -------------- */ -+ u32 fqid_rx, fqid_tx; -+ /* list node for linking us into 'hp_cpu' */ -+ struct list_head node; -+ /* Just to check ... */ -+ unsigned int processor_id; -+} ____cacheline_aligned; -+ -+struct hp_cpu { -+ /* identify the cpu we run on; */ -+ unsigned int processor_id; -+ /* root node for the per-cpu list of handlers */ -+ struct list_head handlers; -+ /* list node for linking us into 'hp_cpu_list' */ -+ struct list_head node; -+ /* when repeatedly scanning 'hp_list', each time linking the n'th -+ * handlers together, this is used as per-cpu iterator state */ -+ struct hp_handler *iterator; -+}; -+ -+/* Each cpu has one of these */ -+static DEFINE_PER_CPU(struct hp_cpu, hp_cpus); -+ -+/* links together the hp_cpu structs, in first-come first-serve order. */ -+static LIST_HEAD(hp_cpu_list); -+static spinlock_t hp_lock = __SPIN_LOCK_UNLOCKED(hp_lock); -+ -+static unsigned int hp_cpu_list_length; -+ -+/* the "special" handler, that starts and terminates the test. */ -+static struct hp_handler *special_handler; -+static int loop_counter; -+ -+/* handlers are allocated out of this, so they're properly aligned. */ -+static struct kmem_cache *hp_handler_slab; -+ -+/* this is the frame data */ -+static void *__frame_ptr; -+static u32 *frame_ptr; -+static dma_addr_t frame_dma; -+ -+/* the main function waits on this */ -+static DECLARE_WAIT_QUEUE_HEAD(queue); -+ -+#define HP_PER_CPU 2 -+#define HP_LOOPS 8 -+/* 80 bytes, like a small ethernet frame, and bleeds into a second cacheline */ -+#define HP_NUM_WORDS 80 -+/* First word of the LFSR-based frame data */ -+#define HP_FIRST_WORD 0xabbaf00d -+ -+static inline u32 do_lfsr(u32 prev) -+{ -+ return (prev >> 1) ^ (-(prev & 1u) & 0xd0000001u); -+} -+ -+static void allocate_frame_data(void) -+{ -+ u32 lfsr = HP_FIRST_WORD; -+ int loop; -+ struct platform_device *pdev = platform_device_alloc("foobar", -1); -+ if (!pdev) -+ panic("platform_device_alloc() failed"); -+ if (platform_device_add(pdev)) -+ panic("platform_device_add() failed"); -+ __frame_ptr = kmalloc(4 * HP_NUM_WORDS, GFP_KERNEL); -+ if (!__frame_ptr) -+ panic("kmalloc() failed"); -+ frame_ptr = (void *)(((unsigned long)__frame_ptr + 63) & -+ ~(unsigned long)63); -+ for (loop = 0; loop < HP_NUM_WORDS; loop++) { -+ frame_ptr[loop] = lfsr; -+ lfsr = do_lfsr(lfsr); -+ } -+ frame_dma = dma_map_single(&pdev->dev, frame_ptr, 4 * HP_NUM_WORDS, -+ DMA_BIDIRECTIONAL); -+ platform_device_del(pdev); -+ platform_device_put(pdev); -+} -+ -+static void deallocate_frame_data(void) -+{ -+ kfree(__frame_ptr); -+} -+ -+static inline void process_frame_data(struct hp_handler *handler, -+ const struct qm_fd *fd) -+{ -+ u32 *p = handler->frame_ptr; -+ u32 lfsr = HP_FIRST_WORD; -+ int loop; -+ if (qm_fd_addr_get64(fd) != (handler->addr & 0xffffffffff)) { -+ pr_err("Got 0x%llx expected 0x%llx\n", -+ qm_fd_addr_get64(fd), handler->addr); -+ panic("bad frame address"); -+ } -+ for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) { -+ *p ^= handler->rx_mixer; -+ if (*p != lfsr) -+ panic("corrupt frame data"); -+ *p ^= handler->tx_mixer; -+ lfsr = do_lfsr(lfsr); -+ } -+} -+ -+static enum qman_cb_dqrr_result normal_dqrr(struct qman_portal *portal, -+ struct qman_fq *fq, -+ const struct qm_dqrr_entry *dqrr) -+{ -+ struct hp_handler *handler = (struct hp_handler *)fq; -+ -+ process_frame_data(handler, &dqrr->fd); -+ if (qman_enqueue(&handler->tx, &dqrr->fd, 0)) -+ panic("qman_enqueue() failed"); -+ return qman_cb_dqrr_consume; -+} -+ -+static enum qman_cb_dqrr_result special_dqrr(struct qman_portal *portal, -+ struct qman_fq *fq, -+ const struct qm_dqrr_entry *dqrr) -+{ -+ struct hp_handler *handler = (struct hp_handler *)fq; -+ -+ process_frame_data(handler, &dqrr->fd); -+ if (++loop_counter < HP_LOOPS) { -+ if (qman_enqueue(&handler->tx, &dqrr->fd, 0)) -+ panic("qman_enqueue() failed"); -+ } else { -+ pr_info("Received final (%dth) frame\n", loop_counter); -+ wake_up(&queue); -+ } -+ return qman_cb_dqrr_consume; -+} -+ -+static void create_per_cpu_handlers(void) -+{ -+ struct hp_handler *handler; -+ int loop; -+ struct hp_cpu *hp_cpu = &get_cpu_var(hp_cpus); -+ -+ hp_cpu->processor_id = smp_processor_id(); -+ spin_lock(&hp_lock); -+ list_add_tail(&hp_cpu->node, &hp_cpu_list); -+ hp_cpu_list_length++; -+ spin_unlock(&hp_lock); -+ INIT_LIST_HEAD(&hp_cpu->handlers); -+ for (loop = 0; loop < HP_PER_CPU; loop++) { -+ handler = kmem_cache_alloc(hp_handler_slab, GFP_KERNEL); -+ if (!handler) -+ panic("kmem_cache_alloc() failed"); -+ handler->processor_id = hp_cpu->processor_id; -+ handler->addr = frame_dma; -+ handler->frame_ptr = frame_ptr; -+ list_add_tail(&handler->node, &hp_cpu->handlers); -+ } -+ put_cpu_var(hp_cpus); -+} -+ -+static void destroy_per_cpu_handlers(void) -+{ -+ struct list_head *loop, *tmp; -+ struct hp_cpu *hp_cpu = &get_cpu_var(hp_cpus); -+ -+ spin_lock(&hp_lock); -+ list_del(&hp_cpu->node); -+ spin_unlock(&hp_lock); -+ list_for_each_safe(loop, tmp, &hp_cpu->handlers) { -+ u32 flags; -+ struct hp_handler *handler = list_entry(loop, struct hp_handler, -+ node); -+ if (qman_retire_fq(&handler->rx, &flags)) -+ panic("qman_retire_fq(rx) failed"); -+ BUG_ON(flags & QMAN_FQ_STATE_BLOCKOOS); -+ if (qman_oos_fq(&handler->rx)) -+ panic("qman_oos_fq(rx) failed"); -+ qman_destroy_fq(&handler->rx, 0); -+ qman_destroy_fq(&handler->tx, 0); -+ qman_release_fqid(handler->fqid_rx); -+ list_del(&handler->node); -+ kmem_cache_free(hp_handler_slab, handler); -+ } -+ put_cpu_var(hp_cpus); -+} -+ -+static inline u8 num_cachelines(u32 offset) -+{ -+ u8 res = (offset + (L1_CACHE_BYTES - 1)) -+ / (L1_CACHE_BYTES); -+ if (res > 3) -+ return 3; -+ return res; -+} -+#define STASH_DATA_CL \ -+ num_cachelines(HP_NUM_WORDS * 4) -+#define STASH_CTX_CL \ -+ num_cachelines(offsetof(struct hp_handler, fqid_rx)) -+ -+static void init_handler(void *__handler) -+{ -+ struct qm_mcc_initfq opts; -+ struct hp_handler *handler = __handler; -+ BUG_ON(handler->processor_id != smp_processor_id()); -+ /* Set up rx */ -+ memset(&handler->rx, 0, sizeof(handler->rx)); -+ if (handler == special_handler) -+ handler->rx.cb.dqrr = special_dqrr; -+ else -+ handler->rx.cb.dqrr = normal_dqrr; -+ if (qman_create_fq(handler->fqid_rx, 0, &handler->rx)) -+ panic("qman_create_fq(rx) failed"); -+ memset(&opts, 0, sizeof(opts)); -+ opts.we_mask = QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_CONTEXTA; -+ opts.fqd.fq_ctrl = QM_FQCTRL_CTXASTASHING; -+ opts.fqd.context_a.stashing.data_cl = STASH_DATA_CL; -+ opts.fqd.context_a.stashing.context_cl = STASH_CTX_CL; -+ if (qman_init_fq(&handler->rx, QMAN_INITFQ_FLAG_SCHED | -+ QMAN_INITFQ_FLAG_LOCAL, &opts)) -+ panic("qman_init_fq(rx) failed"); -+ /* Set up tx */ -+ memset(&handler->tx, 0, sizeof(handler->tx)); -+ if (qman_create_fq(handler->fqid_tx, QMAN_FQ_FLAG_NO_MODIFY, -+ &handler->tx)) -+ panic("qman_create_fq(tx) failed"); -+} -+ -+static void init_phase2(void) -+{ -+ int loop; -+ u32 fqid = 0; -+ u32 lfsr = 0xdeadbeef; -+ struct hp_cpu *hp_cpu; -+ struct hp_handler *handler; -+ -+ for (loop = 0; loop < HP_PER_CPU; loop++) { -+ list_for_each_entry(hp_cpu, &hp_cpu_list, node) { -+ int ret; -+ if (!loop) -+ hp_cpu->iterator = list_first_entry( -+ &hp_cpu->handlers, -+ struct hp_handler, node); -+ else -+ hp_cpu->iterator = list_entry( -+ hp_cpu->iterator->node.next, -+ struct hp_handler, node); -+ /* Rx FQID is the previous handler's Tx FQID */ -+ hp_cpu->iterator->fqid_rx = fqid; -+ /* Allocate new FQID for Tx */ -+ ret = qman_alloc_fqid(&fqid); -+ if (ret) -+ panic("qman_alloc_fqid() failed"); -+ hp_cpu->iterator->fqid_tx = fqid; -+ /* Rx mixer is the previous handler's Tx mixer */ -+ hp_cpu->iterator->rx_mixer = lfsr; -+ /* Get new mixer for Tx */ -+ lfsr = do_lfsr(lfsr); -+ hp_cpu->iterator->tx_mixer = lfsr; -+ } -+ } -+ /* Fix up the first handler (fqid_rx==0, rx_mixer=0xdeadbeef) */ -+ hp_cpu = list_first_entry(&hp_cpu_list, struct hp_cpu, node); -+ handler = list_first_entry(&hp_cpu->handlers, struct hp_handler, node); -+ BUG_ON((handler->fqid_rx != 0) || (handler->rx_mixer != 0xdeadbeef)); -+ handler->fqid_rx = fqid; -+ handler->rx_mixer = lfsr; -+ /* and tag it as our "special" handler */ -+ special_handler = handler; -+} -+ -+static void init_phase3(void) -+{ -+ int loop; -+ struct hp_cpu *hp_cpu; -+ -+ for (loop = 0; loop < HP_PER_CPU; loop++) { -+ list_for_each_entry(hp_cpu, &hp_cpu_list, node) { -+ if (!loop) -+ hp_cpu->iterator = list_first_entry( -+ &hp_cpu->handlers, -+ struct hp_handler, node); -+ else -+ hp_cpu->iterator = list_entry( -+ hp_cpu->iterator->node.next, -+ struct hp_handler, node); -+ preempt_disable(); -+ if (hp_cpu->processor_id == smp_processor_id()) -+ init_handler(hp_cpu->iterator); -+ else -+ smp_call_function_single(hp_cpu->processor_id, -+ init_handler, hp_cpu->iterator, 1); -+ preempt_enable(); -+ } -+ } -+} -+ -+static void send_first_frame(void *ignore) -+{ -+ u32 *p = special_handler->frame_ptr; -+ u32 lfsr = HP_FIRST_WORD; -+ int loop; -+ struct qm_fd fd; -+ -+ BUG_ON(special_handler->processor_id != smp_processor_id()); -+ memset(&fd, 0, sizeof(fd)); -+ qm_fd_addr_set64(&fd, special_handler->addr); -+ fd.format = qm_fd_contig_big; -+ fd.length29 = HP_NUM_WORDS * 4; -+ for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) { -+ if (*p != lfsr) -+ panic("corrupt frame data"); -+ *p ^= special_handler->tx_mixer; -+ lfsr = do_lfsr(lfsr); -+ } -+ pr_info("Sending first frame\n"); -+ if (qman_enqueue(&special_handler->tx, &fd, 0)) -+ panic("qman_enqueue() failed"); -+} -+ -+void qman_test_hotpotato(void) -+{ -+ if (cpumask_weight(cpu_online_mask) < 2) { -+ pr_info("qman_test_hotpotato, skip - only 1 CPU\n"); -+ return; -+ } -+ -+ pr_info("qman_test_hotpotato starting\n"); -+ -+ hp_cpu_list_length = 0; -+ loop_counter = 0; -+ hp_handler_slab = kmem_cache_create("hp_handler_slab", -+ sizeof(struct hp_handler), L1_CACHE_BYTES, -+ SLAB_HWCACHE_ALIGN, NULL); -+ if (!hp_handler_slab) -+ panic("kmem_cache_create() failed"); -+ -+ allocate_frame_data(); -+ -+ /* Init phase 1 */ -+ pr_info("Creating %d handlers per cpu...\n", HP_PER_CPU); -+ if (on_all_cpus(create_per_cpu_handlers)) -+ panic("on_each_cpu() failed"); -+ pr_info("Number of cpus: %d, total of %d handlers\n", -+ hp_cpu_list_length, hp_cpu_list_length * HP_PER_CPU); -+ -+ init_phase2(); -+ -+ init_phase3(); -+ -+ preempt_disable(); -+ if (special_handler->processor_id == smp_processor_id()) -+ send_first_frame(NULL); -+ else -+ smp_call_function_single(special_handler->processor_id, -+ send_first_frame, NULL, 1); -+ preempt_enable(); -+ -+ wait_event(queue, loop_counter == HP_LOOPS); -+ deallocate_frame_data(); -+ if (on_all_cpus(destroy_per_cpu_handlers)) -+ panic("on_each_cpu() failed"); -+ kmem_cache_destroy(hp_handler_slab); -+ pr_info("qman_test_hotpotato finished\n"); -+} ---- /dev/null -+++ b/drivers/staging/fsl_qbman/qman_utility.c -@@ -0,0 +1,129 @@ -+/* Copyright 2008-2011 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "qman_private.h" -+ -+/* ----------------- */ -+/* --- FQID Pool --- */ -+ -+struct qman_fqid_pool { -+ /* Base and size of the FQID range */ -+ u32 fqid_base; -+ u32 total; -+ /* Number of FQIDs currently "allocated" */ -+ u32 used; -+ /* Allocation optimisation. When 'used<total', it is the index of an -+ * available FQID. Otherwise there are no available FQIDs, and this -+ * will be set when the next deallocation occurs. */ -+ u32 next; -+ /* A bit-field representation of the FQID range. */ -+ unsigned long *bits; -+}; -+ -+#define QLONG_BYTES sizeof(unsigned long) -+#define QLONG_BITS (QLONG_BYTES * 8) -+/* Number of 'longs' required for the given number of bits */ -+#define QNUM_LONGS(b) (((b) + QLONG_BITS - 1) / QLONG_BITS) -+/* Shorthand for the number of bytes of same (kmalloc, memset, etc) */ -+#define QNUM_BYTES(b) (QNUM_LONGS(b) * QLONG_BYTES) -+/* And in bits */ -+#define QNUM_BITS(b) (QNUM_LONGS(b) * QLONG_BITS) -+ -+struct qman_fqid_pool *qman_fqid_pool_create(u32 fqid_start, u32 num) -+{ -+ struct qman_fqid_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL); -+ unsigned int i; -+ -+ BUG_ON(!num); -+ if (!pool) -+ return NULL; -+ pool->fqid_base = fqid_start; -+ pool->total = num; -+ pool->used = 0; -+ pool->next = 0; -+ pool->bits = kzalloc(QNUM_BYTES(num), GFP_KERNEL); -+ if (!pool->bits) { -+ kfree(pool); -+ return NULL; -+ } -+ /* If num is not an even multiple of QLONG_BITS (or even 8, for -+ * byte-oriented searching) then we fill the trailing bits with 1, to -+ * make them look allocated (permanently). */ -+ for (i = num + 1; i < QNUM_BITS(num); i++) -+ set_bit(i, pool->bits); -+ return pool; -+} -+EXPORT_SYMBOL(qman_fqid_pool_create); -+ -+int qman_fqid_pool_destroy(struct qman_fqid_pool *pool) -+{ -+ int ret = pool->used; -+ kfree(pool->bits); -+ kfree(pool); -+ return ret; -+} -+EXPORT_SYMBOL(qman_fqid_pool_destroy); -+ -+int qman_fqid_pool_alloc(struct qman_fqid_pool *pool, u32 *fqid) -+{ -+ int ret; -+ if (pool->used == pool->total) -+ return -ENOMEM; -+ *fqid = pool->fqid_base + pool->next; -+ ret = test_and_set_bit(pool->next, pool->bits); -+ BUG_ON(ret); -+ if (++pool->used == pool->total) -+ return 0; -+ pool->next = find_next_zero_bit(pool->bits, pool->total, pool->next); -+ if (pool->next >= pool->total) -+ pool->next = find_first_zero_bit(pool->bits, pool->total); -+ BUG_ON(pool->next >= pool->total); -+ return 0; -+} -+EXPORT_SYMBOL(qman_fqid_pool_alloc); -+ -+void qman_fqid_pool_free(struct qman_fqid_pool *pool, u32 fqid) -+{ -+ int ret; -+ -+ fqid -= pool->fqid_base; -+ ret = test_and_clear_bit(fqid, pool->bits); -+ BUG_ON(!ret); -+ if (pool->used-- == pool->total) -+ pool->next = fqid; -+} -+EXPORT_SYMBOL(qman_fqid_pool_free); -+ -+u32 qman_fqid_pool_used(struct qman_fqid_pool *pool) -+{ -+ return pool->used; -+} -+EXPORT_SYMBOL(qman_fqid_pool_used); ---- /dev/null -+++ b/include/linux/fsl_bman.h -@@ -0,0 +1,532 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef FSL_BMAN_H -+#define FSL_BMAN_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+/* Last updated for v00.79 of the BG */ -+ -+/* Portal processing (interrupt) sources */ -+#define BM_PIRQ_RCRI 0x00000002 /* RCR Ring (below threshold) */ -+#define BM_PIRQ_BSCN 0x00000001 /* Buffer depletion State Change */ -+ -+/* This wrapper represents a bit-array for the depletion state of the 64 Bman -+ * buffer pools. */ -+struct bman_depletion { -+ u32 __state[2]; -+}; -+#define BMAN_DEPLETION_EMPTY { { 0x00000000, 0x00000000 } } -+#define BMAN_DEPLETION_FULL { { 0xffffffff, 0xffffffff } } -+#define __bmdep_word(x) ((x) >> 5) -+#define __bmdep_shift(x) ((x) & 0x1f) -+#define __bmdep_bit(x) (0x80000000 >> __bmdep_shift(x)) -+static inline void bman_depletion_init(struct bman_depletion *c) -+{ -+ c->__state[0] = c->__state[1] = 0; -+} -+static inline void bman_depletion_fill(struct bman_depletion *c) -+{ -+ c->__state[0] = c->__state[1] = ~0; -+} -+static inline int bman_depletion_get(const struct bman_depletion *c, u8 bpid) -+{ -+ return c->__state[__bmdep_word(bpid)] & __bmdep_bit(bpid); -+} -+static inline void bman_depletion_set(struct bman_depletion *c, u8 bpid) -+{ -+ c->__state[__bmdep_word(bpid)] |= __bmdep_bit(bpid); -+} -+static inline void bman_depletion_unset(struct bman_depletion *c, u8 bpid) -+{ -+ c->__state[__bmdep_word(bpid)] &= ~__bmdep_bit(bpid); -+} -+ -+/* ------------------------------------------------------- */ -+/* --- Bman data structures (and associated constants) --- */ -+ -+/* Represents s/w corenet portal mapped data structures */ -+struct bm_rcr_entry; /* RCR (Release Command Ring) entries */ -+struct bm_mc_command; /* MC (Management Command) command */ -+struct bm_mc_result; /* MC result */ -+ -+/* Code-reduction, define a wrapper for 48-bit buffers. In cases where a buffer -+ * pool id specific to this buffer is needed (BM_RCR_VERB_CMD_BPID_MULTI, -+ * BM_MCC_VERB_ACQUIRE), the 'bpid' field is used. */ -+struct bm_buffer { -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 __reserved1; -+ u8 bpid; -+ u16 hi; /* High 16-bits of 48-bit address */ -+ u32 lo; /* Low 32-bits of 48-bit address */ -+#else -+ u32 lo; -+ u16 hi; -+ u8 bpid; -+ u8 __reserved; -+#endif -+ }; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u64 __notaddress:16; -+ u64 addr:48; -+#else -+ u64 addr:48; -+ u64 __notaddress:16; -+#endif -+ }; -+ u64 opaque; -+ }; -+} __aligned(8); -+static inline u64 bm_buffer_get64(const struct bm_buffer *buf) -+{ -+ return buf->addr; -+} -+static inline dma_addr_t bm_buf_addr(const struct bm_buffer *buf) -+{ -+ return (dma_addr_t)buf->addr; -+} -+/* Macro, so we compile better if 'v' isn't always 64-bit */ -+#define bm_buffer_set64(buf, v) \ -+ do { \ -+ struct bm_buffer *__buf931 = (buf); \ -+ __buf931->hi = upper_32_bits(v); \ -+ __buf931->lo = lower_32_bits(v); \ -+ } while (0) -+ -+/* See 1.5.3.5.4: "Release Command" */ -+struct bm_rcr_entry { -+ union { -+ struct { -+ u8 __dont_write_directly__verb; -+ u8 bpid; /* used with BM_RCR_VERB_CMD_BPID_SINGLE */ -+ u8 __reserved1[62]; -+ }; -+ struct bm_buffer bufs[8]; -+ }; -+} __packed; -+#define BM_RCR_VERB_VBIT 0x80 -+#define BM_RCR_VERB_CMD_MASK 0x70 /* one of two values; */ -+#define BM_RCR_VERB_CMD_BPID_SINGLE 0x20 -+#define BM_RCR_VERB_CMD_BPID_MULTI 0x30 -+#define BM_RCR_VERB_BUFCOUNT_MASK 0x0f /* values 1..8 */ -+ -+/* See 1.5.3.1: "Acquire Command" */ -+/* See 1.5.3.2: "Query Command" */ -+struct bm_mcc_acquire { -+ u8 bpid; -+ u8 __reserved1[62]; -+} __packed; -+struct bm_mcc_query { -+ u8 __reserved2[63]; -+} __packed; -+struct bm_mc_command { -+ u8 __dont_write_directly__verb; -+ union { -+ struct bm_mcc_acquire acquire; -+ struct bm_mcc_query query; -+ }; -+} __packed; -+#define BM_MCC_VERB_VBIT 0x80 -+#define BM_MCC_VERB_CMD_MASK 0x70 /* where the verb contains; */ -+#define BM_MCC_VERB_CMD_ACQUIRE 0x10 -+#define BM_MCC_VERB_CMD_QUERY 0x40 -+#define BM_MCC_VERB_ACQUIRE_BUFCOUNT 0x0f /* values 1..8 go here */ -+ -+/* See 1.5.3.3: "Acquire Response" */ -+/* See 1.5.3.4: "Query Response" */ -+struct bm_pool_state { -+ u8 __reserved1[32]; -+ /* "availability state" and "depletion state" */ -+ struct { -+ u8 __reserved1[8]; -+ /* Access using bman_depletion_***() */ -+ struct bman_depletion state; -+ } as, ds; -+}; -+struct bm_mc_result { -+ union { -+ struct { -+ u8 verb; -+ u8 __reserved1[63]; -+ }; -+ union { -+ struct { -+ u8 __reserved1; -+ u8 bpid; -+ u8 __reserved2[62]; -+ }; -+ struct bm_buffer bufs[8]; -+ } acquire; -+ struct bm_pool_state query; -+ }; -+} __packed; -+#define BM_MCR_VERB_VBIT 0x80 -+#define BM_MCR_VERB_CMD_MASK BM_MCC_VERB_CMD_MASK -+#define BM_MCR_VERB_CMD_ACQUIRE BM_MCC_VERB_CMD_ACQUIRE -+#define BM_MCR_VERB_CMD_QUERY BM_MCC_VERB_CMD_QUERY -+#define BM_MCR_VERB_CMD_ERR_INVALID 0x60 -+#define BM_MCR_VERB_CMD_ERR_ECC 0x70 -+#define BM_MCR_VERB_ACQUIRE_BUFCOUNT BM_MCC_VERB_ACQUIRE_BUFCOUNT /* 0..8 */ -+/* Determine the "availability state" of pool 'p' from a query result 'r' */ -+#define BM_MCR_QUERY_AVAILABILITY(r, p) \ -+ bman_depletion_get(&r->query.as.state, p) -+/* Determine the "depletion state" of pool 'p' from a query result 'r' */ -+#define BM_MCR_QUERY_DEPLETION(r, p) \ -+ bman_depletion_get(&r->query.ds.state, p) -+ -+/*******************************************************************/ -+/* Managed (aka "shared" or "mux/demux") portal, high-level i/face */ -+/*******************************************************************/ -+ -+ /* Portal and Buffer Pools */ -+ /* ----------------------- */ -+/* Represents a managed portal */ -+struct bman_portal; -+ -+/* This object type represents Bman buffer pools. */ -+struct bman_pool; -+ -+struct bman_portal_config { -+ /* This is used for any "core-affine" portals, ie. default portals -+ * associated to the corresponding cpu. -1 implies that there is no core -+ * affinity configured. */ -+ int cpu; -+ /* portal interrupt line */ -+ int irq; -+ /* the unique index of this portal */ -+ u32 index; -+ /* Is this portal shared? (If so, it has coarser locking and demuxes -+ * processing on behalf of other CPUs.) */ -+ int is_shared; -+ /* These are the buffer pool IDs that may be used via this portal. */ -+ struct bman_depletion mask; -+}; -+ -+/* This callback type is used when handling pool depletion entry/exit. The -+ * 'cb_ctx' value is the opaque value associated with the pool object in -+ * bman_new_pool(). 'depleted' is non-zero on depletion-entry, and zero on -+ * depletion-exit. */ -+typedef void (*bman_cb_depletion)(struct bman_portal *bm, -+ struct bman_pool *pool, void *cb_ctx, int depleted); -+ -+/* This struct specifies parameters for a bman_pool object. */ -+struct bman_pool_params { -+ /* index of the buffer pool to encapsulate (0-63), ignored if -+ * BMAN_POOL_FLAG_DYNAMIC_BPID is set. */ -+ u32 bpid; -+ /* bit-mask of BMAN_POOL_FLAG_*** options */ -+ u32 flags; -+ /* depletion-entry/exit callback, if BMAN_POOL_FLAG_DEPLETION is set */ -+ bman_cb_depletion cb; -+ /* opaque user value passed as a parameter to 'cb' */ -+ void *cb_ctx; -+ /* depletion-entry/exit thresholds, if BMAN_POOL_FLAG_THRESH is set. NB: -+ * this is only allowed if BMAN_POOL_FLAG_DYNAMIC_BPID is used *and* -+ * when run in the control plane (which controls Bman CCSR). This array -+ * matches the definition of bm_pool_set(). */ -+ u32 thresholds[4]; -+}; -+ -+/* Flags to bman_new_pool() */ -+#define BMAN_POOL_FLAG_NO_RELEASE 0x00000001 /* can't release to pool */ -+#define BMAN_POOL_FLAG_ONLY_RELEASE 0x00000002 /* can only release to pool */ -+#define BMAN_POOL_FLAG_DEPLETION 0x00000004 /* track depletion entry/exit */ -+#define BMAN_POOL_FLAG_DYNAMIC_BPID 0x00000008 /* (de)allocate bpid */ -+#define BMAN_POOL_FLAG_THRESH 0x00000010 /* set depletion thresholds */ -+#define BMAN_POOL_FLAG_STOCKPILE 0x00000020 /* stockpile to reduce hw ops */ -+ -+/* Flags to bman_release() */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+#define BMAN_RELEASE_FLAG_WAIT 0x00000001 /* wait if RCR is full */ -+#define BMAN_RELEASE_FLAG_WAIT_INT 0x00000002 /* if we wait, interruptible? */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+#define BMAN_RELEASE_FLAG_WAIT_SYNC 0x00000004 /* if wait, until consumed? */ -+#endif -+#endif -+#define BMAN_RELEASE_FLAG_NOW 0x00000008 /* issue immediate release */ -+ -+/* Flags to bman_acquire() */ -+#define BMAN_ACQUIRE_FLAG_STOCKPILE 0x00000001 /* no hw op, stockpile only */ -+ -+ /* Portal Management */ -+ /* ----------------- */ -+/** -+ * bman_get_portal_config - get portal configuration settings -+ * -+ * This returns a read-only view of the current cpu's affine portal settings. -+ */ -+const struct bman_portal_config *bman_get_portal_config(void); -+ -+/** -+ * bman_irqsource_get - return the portal work that is interrupt-driven -+ * -+ * Returns a bitmask of BM_PIRQ_**I processing sources that are currently -+ * enabled for interrupt handling on the current cpu's affine portal. These -+ * sources will trigger the portal interrupt and the interrupt handler (or a -+ * tasklet/bottom-half it defers to) will perform the corresponding processing -+ * work. The bman_poll_***() functions will only process sources that are not in -+ * this bitmask. If the current CPU is sharing a portal hosted on another CPU, -+ * this always returns zero. -+ */ -+u32 bman_irqsource_get(void); -+ -+/** -+ * bman_irqsource_add - add processing sources to be interrupt-driven -+ * @bits: bitmask of BM_PIRQ_**I processing sources -+ * -+ * Adds processing sources that should be interrupt-driven (rather than -+ * processed via bman_poll_***() functions). Returns zero for success, or -+ * -EINVAL if the current CPU is sharing a portal hosted on another CPU. */ -+int bman_irqsource_add(u32 bits); -+ -+/** -+ * bman_irqsource_remove - remove processing sources from being interrupt-driven -+ * @bits: bitmask of BM_PIRQ_**I processing sources -+ * -+ * Removes processing sources from being interrupt-driven, so that they will -+ * instead be processed via bman_poll_***() functions. Returns zero for success, -+ * or -EINVAL if the current CPU is sharing a portal hosted on another CPU. */ -+int bman_irqsource_remove(u32 bits); -+ -+/** -+ * bman_affine_cpus - return a mask of cpus that have affine portals -+ */ -+const cpumask_t *bman_affine_cpus(void); -+ -+/** -+ * bman_poll_slow - process anything that isn't interrupt-driven. -+ * -+ * This function does any portal processing that isn't interrupt-driven. If the -+ * current CPU is sharing a portal hosted on another CPU, this function will -+ * return -EINVAL, otherwise the return value is a bitmask of BM_PIRQ_* sources -+ * indicating what interrupt sources were actually processed by the call. -+ * -+ * NB, unlike the legacy wrapper bman_poll(), this function will -+ * deterministically check for the presence of portal processing work and do it, -+ * which implies some latency even if there's nothing to do. The bman_poll() -+ * wrapper on the other hand (like the qman_poll() wrapper) attenuates this by -+ * checking for (and doing) portal processing infrequently. Ie. such that -+ * qman_poll() and bman_poll() can be called from core-processing loops. Use -+ * bman_poll_slow() when you yourself are deciding when to incur the overhead of -+ * processing. -+ */ -+u32 bman_poll_slow(void); -+ -+/** -+ * bman_poll - process anything that isn't interrupt-driven. -+ * -+ * Dispatcher logic on a cpu can use this to trigger any maintenance of the -+ * affine portal. This function does whatever processing is not triggered by -+ * interrupts. This is a legacy wrapper that can be used in core-processing -+ * loops but mitigates the performance overhead of portal processing by -+ * adaptively bypassing true portal processing most of the time. (Processing is -+ * done once every 10 calls if the previous processing revealed that work needed -+ * to be done, or once very 1000 calls if the previous processing revealed no -+ * work needed doing.) If you wish to control this yourself, call -+ * bman_poll_slow() instead, which always checks for portal processing work. -+ */ -+void bman_poll(void); -+ -+/** -+ * bman_rcr_is_empty - Determine if portal's RCR is empty -+ * -+ * For use in situations where a cpu-affine caller needs to determine when all -+ * releases for the local portal have been processed by Bman but can't use the -+ * BMAN_RELEASE_FLAG_WAIT_SYNC flag to do this from the final bman_release(). -+ * The function forces tracking of RCR consumption (which normally doesn't -+ * happen until release processing needs to find space to put new release -+ * commands), and returns zero if the ring still has unprocessed entries, -+ * non-zero if it is empty. -+ */ -+int bman_rcr_is_empty(void); -+ -+/** -+ * bman_alloc_bpid_range - Allocate a contiguous range of BPIDs -+ * @result: is set by the API to the base BPID of the allocated range -+ * @count: the number of BPIDs required -+ * @align: required alignment of the allocated range -+ * @partial: non-zero if the API can return fewer than @count BPIDs -+ * -+ * Returns the number of buffer pools allocated, or a negative error code. If -+ * @partial is non zero, the allocation request may return a smaller range of -+ * BPs than requested (though alignment will be as requested). If @partial is -+ * zero, the return value will either be 'count' or negative. -+ */ -+int bman_alloc_bpid_range(u32 *result, u32 count, u32 align, int partial); -+static inline int bman_alloc_bpid(u32 *result) -+{ -+ int ret = bman_alloc_bpid_range(result, 1, 0, 0); -+ return (ret > 0) ? 0 : ret; -+} -+ -+/** -+ * bman_release_bpid_range - Release the specified range of buffer pool IDs -+ * @bpid: the base BPID of the range to deallocate -+ * @count: the number of BPIDs in the range -+ * -+ * This function can also be used to seed the allocator with ranges of BPIDs -+ * that it can subsequently allocate from. -+ */ -+void bman_release_bpid_range(u32 bpid, unsigned int count); -+static inline void bman_release_bpid(u32 bpid) -+{ -+ bman_release_bpid_range(bpid, 1); -+} -+ -+int bman_reserve_bpid_range(u32 bpid, unsigned int count); -+static inline int bman_reserve_bpid(u32 bpid) -+{ -+ return bman_reserve_bpid_range(bpid, 1); -+} -+ -+void bman_seed_bpid_range(u32 bpid, unsigned int count); -+ -+ -+int bman_shutdown_pool(u32 bpid); -+ -+ /* Pool management */ -+ /* --------------- */ -+/** -+ * bman_new_pool - Allocates a Buffer Pool object -+ * @params: parameters specifying the buffer pool ID and behaviour -+ * -+ * Creates a pool object for the given @params. A portal and the depletion -+ * callback field of @params are only used if the BMAN_POOL_FLAG_DEPLETION flag -+ * is set. NB, the fields from @params are copied into the new pool object, so -+ * the structure provided by the caller can be released or reused after the -+ * function returns. -+ */ -+struct bman_pool *bman_new_pool(const struct bman_pool_params *params); -+ -+/** -+ * bman_free_pool - Deallocates a Buffer Pool object -+ * @pool: the pool object to release -+ * -+ */ -+void bman_free_pool(struct bman_pool *pool); -+ -+/** -+ * bman_get_params - Returns a pool object's parameters. -+ * @pool: the pool object -+ * -+ * The returned pointer refers to state within the pool object so must not be -+ * modified and can no longer be read once the pool object is destroyed. -+ */ -+const struct bman_pool_params *bman_get_params(const struct bman_pool *pool); -+ -+/** -+ * bman_release - Release buffer(s) to the buffer pool -+ * @pool: the buffer pool object to release to -+ * @bufs: an array of buffers to release -+ * @num: the number of buffers in @bufs (1-8) -+ * @flags: bit-mask of BMAN_RELEASE_FLAG_*** options -+ * -+ * Adds the given buffers to RCR entries. If the portal @p was created with the -+ * "COMPACT" flag, then it will be using a compaction algorithm to improve -+ * utilisation of RCR. As such, these buffers may join an existing ring entry -+ * and/or it may not be issued right away so as to allow future releases to join -+ * the same ring entry. Use the BMAN_RELEASE_FLAG_NOW flag to override this -+ * behaviour by committing the RCR entry (or entries) right away. If the RCR -+ * ring is full, the function will return -EBUSY unless BMAN_RELEASE_FLAG_WAIT -+ * is selected, in which case it will sleep waiting for space to become -+ * available in RCR. If the function receives a signal before such time (and -+ * BMAN_RELEASE_FLAG_WAIT_INT is set), the function returns -EINTR. Otherwise, -+ * it returns zero. -+ */ -+int bman_release(struct bman_pool *pool, const struct bm_buffer *bufs, u8 num, -+ u32 flags); -+ -+/** -+ * bman_acquire - Acquire buffer(s) from a buffer pool -+ * @pool: the buffer pool object to acquire from -+ * @bufs: array for storing the acquired buffers -+ * @num: the number of buffers desired (@bufs is at least this big) -+ * -+ * Issues an "Acquire" command via the portal's management command interface. -+ * The return value will be the number of buffers obtained from the pool, or a -+ * negative error code if a h/w error or pool starvation was encountered. In -+ * the latter case, the content of @bufs is undefined. -+ */ -+int bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, u8 num, -+ u32 flags); -+ -+/** -+ * bman_flush_stockpile - Flush stockpile buffer(s) to the buffer pool -+ * @pool: the buffer pool object the stockpile belongs -+ * @flags: bit-mask of BMAN_RELEASE_FLAG_*** options -+ * -+ * Adds stockpile buffers to RCR entries until the stockpile is empty. -+ * The return value will be a negative error code if a h/w error occurred. -+ * If BMAN_RELEASE_FLAG_NOW flag is passed and RCR ring is full, -+ * -EAGAIN will be returned. -+ */ -+int bman_flush_stockpile(struct bman_pool *pool, u32 flags); -+ -+/** -+ * bman_query_pools - Query all buffer pool states -+ * @state: storage for the queried availability and depletion states -+ */ -+int bman_query_pools(struct bm_pool_state *state); -+ -+#ifdef CONFIG_FSL_BMAN_CONFIG -+/** -+ * bman_query_free_buffers - Query how many free buffers are in buffer pool -+ * @pool: the buffer pool object to query -+ * -+ * Return the number of the free buffers -+ */ -+u32 bman_query_free_buffers(struct bman_pool *pool); -+ -+/** -+ * bman_update_pool_thresholds - Change the buffer pool's depletion thresholds -+ * @pool: the buffer pool object to which the thresholds will be set -+ * @thresholds: the new thresholds -+ */ -+int bman_update_pool_thresholds(struct bman_pool *pool, const u32 *thresholds); -+#endif -+ -+/** -+ * The below bman_p_***() variant might be called in a situation that the cpu -+ * which the portal affine to is not online yet. -+ * @bman_portal specifies which portal the API will use. -+*/ -+int bman_p_irqsource_add(struct bman_portal *p, __maybe_unused u32 bits); -+#ifdef __cplusplus -+} -+#endif -+ -+#endif /* FSL_BMAN_H */ ---- /dev/null -+++ b/include/linux/fsl_qman.h -@@ -0,0 +1,3888 @@ -+/* Copyright 2008-2012 Freescale Semiconductor, Inc. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Freescale Semiconductor nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY -+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef FSL_QMAN_H -+#define FSL_QMAN_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+/* Last updated for v00.800 of the BG */ -+ -+/* Hardware constants */ -+#define QM_CHANNEL_SWPORTAL0 0 -+#define QMAN_CHANNEL_POOL1 0x21 -+#define QMAN_CHANNEL_CAAM 0x80 -+#define QMAN_CHANNEL_PME 0xa0 -+#define QMAN_CHANNEL_POOL1_REV3 0x401 -+#define QMAN_CHANNEL_CAAM_REV3 0x840 -+#define QMAN_CHANNEL_PME_REV3 0x860 -+#define QMAN_CHANNEL_DCE 0x8a0 -+#define QMAN_CHANNEL_DCE_QMANREV312 0x880 -+extern u16 qm_channel_pool1; -+extern u16 qm_channel_caam; -+extern u16 qm_channel_pme; -+extern u16 qm_channel_dce; -+enum qm_dc_portal { -+ qm_dc_portal_fman0 = 0, -+ qm_dc_portal_fman1 = 1, -+ qm_dc_portal_caam = 2, -+ qm_dc_portal_pme = 3, -+ qm_dc_portal_rman = 4, -+ qm_dc_portal_dce = 5 -+}; -+ -+/* Portal processing (interrupt) sources */ -+#define QM_PIRQ_CCSCI 0x00200000 /* CEETM Congestion State Change */ -+#define QM_PIRQ_CSCI 0x00100000 /* Congestion State Change */ -+#define QM_PIRQ_EQCI 0x00080000 /* Enqueue Command Committed */ -+#define QM_PIRQ_EQRI 0x00040000 /* EQCR Ring (below threshold) */ -+#define QM_PIRQ_DQRI 0x00020000 /* DQRR Ring (non-empty) */ -+#define QM_PIRQ_MRI 0x00010000 /* MR Ring (non-empty) */ -+/* This mask contains all the interrupt sources that need handling except DQRI, -+ * ie. that if present should trigger slow-path processing. */ -+#define QM_PIRQ_SLOW (QM_PIRQ_CSCI | QM_PIRQ_EQCI | QM_PIRQ_EQRI | \ -+ QM_PIRQ_MRI | QM_PIRQ_CCSCI) -+ -+/* --- Clock speed --- */ -+/* A qman driver instance may or may not know the current qman clock speed. -+ * However, certain CEETM calculations may not be possible if this is not known. -+ * The 'set' function will only succeed (return zero) if the driver did not -+ * already know the clock speed. Likewise, the 'get' function will only succeed -+ * if the driver does know the clock speed (either because it knew when booting, -+ * or was told via 'set'). In cases where software is running on a driver -+ * instance that does not know the clock speed (eg. on a hypervised data-plane), -+ * and the user can obtain the current qman clock speed by other means (eg. from -+ * a message sent from the control-plane), then the 'set' function can be used -+ * to enable rate-calculations in a driver where it would otherwise not be -+ * possible. */ -+int qm_get_clock(u64 *clock_hz); -+int qm_set_clock(u64 clock_hz); -+ -+/* For qman_static_dequeue_*** APIs */ -+#define QM_SDQCR_CHANNELS_POOL_MASK 0x00007fff -+/* for n in [1,15] */ -+#define QM_SDQCR_CHANNELS_POOL(n) (0x00008000 >> (n)) -+/* for conversion from n of qm_channel */ -+static inline u32 QM_SDQCR_CHANNELS_POOL_CONV(u16 channel) -+{ -+ return QM_SDQCR_CHANNELS_POOL(channel + 1 - qm_channel_pool1); -+} -+ -+/* For qman_volatile_dequeue(); Choose one PRECEDENCE. EXACT is optional. Use -+ * NUMFRAMES(n) (6-bit) or NUMFRAMES_TILLEMPTY to fill in the frame-count. Use -+ * FQID(n) to fill in the frame queue ID. */ -+#define QM_VDQCR_PRECEDENCE_VDQCR 0x0 -+#define QM_VDQCR_PRECEDENCE_SDQCR 0x80000000 -+#define QM_VDQCR_EXACT 0x40000000 -+#define QM_VDQCR_NUMFRAMES_MASK 0x3f000000 -+#define QM_VDQCR_NUMFRAMES_SET(n) (((n) & 0x3f) << 24) -+#define QM_VDQCR_NUMFRAMES_GET(n) (((n) >> 24) & 0x3f) -+#define QM_VDQCR_NUMFRAMES_TILLEMPTY QM_VDQCR_NUMFRAMES_SET(0) -+ -+ -+/* ------------------------------------------------------- */ -+/* --- Qman data structures (and associated constants) --- */ -+ -+/* Represents s/w corenet portal mapped data structures */ -+struct qm_eqcr_entry; /* EQCR (EnQueue Command Ring) entries */ -+struct qm_dqrr_entry; /* DQRR (DeQueue Response Ring) entries */ -+struct qm_mr_entry; /* MR (Message Ring) entries */ -+struct qm_mc_command; /* MC (Management Command) command */ -+struct qm_mc_result; /* MC result */ -+ -+/* See David Lapp's "Frame formats" document, "dpateam", Jan 07, 2008 */ -+#define QM_FD_FORMAT_SG 0x4 -+#define QM_FD_FORMAT_LONG 0x2 -+#define QM_FD_FORMAT_COMPOUND 0x1 -+enum qm_fd_format { -+ /* 'contig' implies a contiguous buffer, whereas 'sg' implies a -+ * scatter-gather table. 'big' implies a 29-bit length with no offset -+ * field, otherwise length is 20-bit and offset is 9-bit. 'compound' -+ * implies a s/g-like table, where each entry itself represents a frame -+ * (contiguous or scatter-gather) and the 29-bit "length" is -+ * interpreted purely for congestion calculations, ie. a "congestion -+ * weight". */ -+ qm_fd_contig = 0, -+ qm_fd_contig_big = QM_FD_FORMAT_LONG, -+ qm_fd_sg = QM_FD_FORMAT_SG, -+ qm_fd_sg_big = QM_FD_FORMAT_SG | QM_FD_FORMAT_LONG, -+ qm_fd_compound = QM_FD_FORMAT_COMPOUND -+}; -+ -+/* Capitalised versions are un-typed but can be used in static expressions */ -+#define QM_FD_CONTIG 0 -+#define QM_FD_CONTIG_BIG QM_FD_FORMAT_LONG -+#define QM_FD_SG QM_FD_FORMAT_SG -+#define QM_FD_SG_BIG (QM_FD_FORMAT_SG | QM_FD_FORMAT_LONG) -+#define QM_FD_COMPOUND QM_FD_FORMAT_COMPOUND -+ -+/* See 1.5.1.1: "Frame Descriptor (FD)" */ -+struct qm_fd { -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 dd:2; /* dynamic debug */ -+ u8 liodn_offset:6; -+ u8 bpid:8; /* Buffer Pool ID */ -+ u8 eliodn_offset:4; -+ u8 __reserved:4; -+ u8 addr_hi; /* high 8-bits of 40-bit address */ -+ u32 addr_lo; /* low 32-bits of 40-bit address */ -+#else -+ u32 addr_lo; /* low 32-bits of 40-bit address */ -+ u8 addr_hi; /* high 8-bits of 40-bit address */ -+ u8 __reserved:4; -+ u8 eliodn_offset:4; -+ u8 bpid:8; /* Buffer Pool ID */ -+ u8 liodn_offset:6; -+ u8 dd:2; /* dynamic debug */ -+#endif -+ }; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u64 __notaddress:24; -+ u64 addr:40; -+#else -+ u64 addr:40; -+ u64 __notaddress:24; -+#endif -+ }; -+ u64 opaque_addr; -+ }; -+ /* The 'format' field indicates the interpretation of the remaining 29 -+ * bits of the 32-bit word. For packing reasons, it is duplicated in the -+ * other union elements. Note, union'd structs are difficult to use with -+ * static initialisation under gcc, in which case use the "opaque" form -+ * with one of the macros. */ -+ union { -+ /* For easier/faster copying of this part of the fd (eg. from a -+ * DQRR entry to an EQCR entry) copy 'opaque' */ -+ u32 opaque; -+ /* If 'format' is _contig or _sg, 20b length and 9b offset */ -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ enum qm_fd_format format:3; -+ u16 offset:9; -+ u32 length20:20; -+#else -+ u32 length20:20; -+ u16 offset:9; -+ enum qm_fd_format format:3; -+#endif -+ }; -+ /* If 'format' is _contig_big or _sg_big, 29b length */ -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ enum qm_fd_format _format1:3; -+ u32 length29:29; -+#else -+ u32 length29:29; -+ enum qm_fd_format _format1:3; -+#endif -+ }; -+ /* If 'format' is _compound, 29b "congestion weight" */ -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ enum qm_fd_format _format2:3; -+ u32 cong_weight:29; -+#else -+ u32 cong_weight:29; -+ enum qm_fd_format _format2:3; -+#endif -+ }; -+ }; -+ union { -+ u32 cmd; -+ u32 status; -+ }; -+} __aligned(8); -+#define QM_FD_DD_NULL 0x00 -+#define QM_FD_PID_MASK 0x3f -+static inline u64 qm_fd_addr_get64(const struct qm_fd *fd) -+{ -+ return fd->addr; -+} -+ -+static inline dma_addr_t qm_fd_addr(const struct qm_fd *fd) -+{ -+ return (dma_addr_t)fd->addr; -+} -+/* Macro, so we compile better if 'v' isn't always 64-bit */ -+#define qm_fd_addr_set64(fd, v) \ -+ do { \ -+ struct qm_fd *__fd931 = (fd); \ -+ __fd931->addr = v; \ -+ } while (0) -+ -+/* For static initialisation of FDs (which is complicated by the use of unions -+ * in "struct qm_fd"), use the following macros. Note that; -+ * - 'dd', 'pid' and 'bpid' are ignored because there's no static initialisation -+ * use-case), -+ * - use capitalised QM_FD_*** formats for static initialisation. -+ */ -+#define QM_FD_FMT_20(cmd, addr_hi, addr_lo, fmt, off, len) \ -+ { 0, 0, 0, 0, 0, addr_hi, addr_lo, \ -+ { (((fmt)&0x7) << 29) | (((off)&0x1ff) << 20) | ((len)&0xfffff) }, \ -+ { cmd } } -+#define QM_FD_FMT_29(cmd, addr_hi, addr_lo, fmt, len) \ -+ { 0, 0, 0, 0, 0, addr_hi, addr_lo, \ -+ { (((fmt)&0x7) << 29) | ((len)&0x1fffffff) }, \ -+ { cmd } } -+ -+/* See 2.2.1.3 Multi-Core Datapath Acceleration Architecture */ -+#define QM_SG_OFFSET_MASK 0x1FFF -+struct qm_sg_entry { -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 __reserved1[3]; -+ u8 addr_hi; /* high 8-bits of 40-bit address */ -+ u32 addr_lo; /* low 32-bits of 40-bit address */ -+#else -+ u32 addr_lo; /* low 32-bits of 40-bit address */ -+ u8 addr_hi; /* high 8-bits of 40-bit address */ -+ u8 __reserved1[3]; -+#endif -+ }; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u64 __notaddress:24; -+ u64 addr:40; -+#else -+ u64 addr:40; -+ u64 __notaddress:24; -+#endif -+ }; -+ u64 opaque; -+ }; -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 extension:1; /* Extension bit */ -+ u32 final:1; /* Final bit */ -+ u32 length:30; -+#else -+ u32 length:30; -+ u32 final:1; /* Final bit */ -+ u32 extension:1; /* Extension bit */ -+#endif -+ }; -+ u32 sgt_efl; -+ }; -+ u8 __reserved2; -+ u8 bpid; -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u16 __reserved3:3; -+ u16 offset:13; -+#else -+ u16 offset:13; -+ u16 __reserved3:3; -+#endif -+ }; -+ u16 opaque_offset; -+ }; -+} __packed; -+union qm_sg_efl { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 extension:1; /* Extension bit */ -+ u32 final:1; /* Final bit */ -+ u32 length:30; -+#else -+ u32 length:30; -+ u32 final:1; /* Final bit */ -+ u32 extension:1; /* Extension bit */ -+#endif -+ }; -+ u32 efl; -+}; -+static inline dma_addr_t qm_sg_addr(const struct qm_sg_entry *sg) -+{ -+ return (dma_addr_t)be64_to_cpu(sg->opaque) & 0xffffffffffULL; -+} -+static inline u8 qm_sg_entry_get_ext(const struct qm_sg_entry *sg) -+{ -+ union qm_sg_efl u; -+ -+ u.efl = be32_to_cpu(sg->sgt_efl); -+ return u.extension; -+} -+static inline u8 qm_sg_entry_get_final(const struct qm_sg_entry *sg) -+{ -+ union qm_sg_efl u; -+ -+ u.efl = be32_to_cpu(sg->sgt_efl); -+ return u.final; -+} -+static inline u32 qm_sg_entry_get_len(const struct qm_sg_entry *sg) -+{ -+ union qm_sg_efl u; -+ -+ u.efl = be32_to_cpu(sg->sgt_efl); -+ return u.length; -+} -+static inline u8 qm_sg_entry_get_bpid(const struct qm_sg_entry *sg) -+{ -+ return sg->bpid; -+} -+static inline u16 qm_sg_entry_get_offset(const struct qm_sg_entry *sg) -+{ -+ u32 opaque_offset = be16_to_cpu(sg->opaque_offset); -+ -+ return opaque_offset & 0x1fff; -+} -+ -+/* Macro, so we compile better if 'v' isn't always 64-bit */ -+#define qm_sg_entry_set64(sg, v) \ -+ do { \ -+ struct qm_sg_entry *__sg931 = (sg); \ -+ __sg931->opaque = cpu_to_be64(v); \ -+ } while (0) -+#define qm_sg_entry_set_ext(sg, v) \ -+ do { \ -+ union qm_sg_efl __u932; \ -+ __u932.efl = be32_to_cpu((sg)->sgt_efl); \ -+ __u932.extension = v; \ -+ (sg)->sgt_efl = cpu_to_be32(__u932.efl); \ -+ } while (0) -+#define qm_sg_entry_set_final(sg, v) \ -+ do { \ -+ union qm_sg_efl __u933; \ -+ __u933.efl = be32_to_cpu((sg)->sgt_efl); \ -+ __u933.final = v; \ -+ (sg)->sgt_efl = cpu_to_be32(__u933.efl); \ -+ } while (0) -+#define qm_sg_entry_set_len(sg, v) \ -+ do { \ -+ union qm_sg_efl __u934; \ -+ __u934.efl = be32_to_cpu((sg)->sgt_efl); \ -+ __u934.length = v; \ -+ (sg)->sgt_efl = cpu_to_be32(__u934.efl); \ -+ } while (0) -+#define qm_sg_entry_set_bpid(sg, v) \ -+ do { \ -+ struct qm_sg_entry *__u935 = (sg); \ -+ __u935->bpid = v; \ -+ } while (0) -+#define qm_sg_entry_set_offset(sg, v) \ -+ do { \ -+ struct qm_sg_entry *__u936 = (sg); \ -+ __u936->opaque_offset = cpu_to_be16(v); \ -+ } while (0) -+ -+/* See 1.5.8.1: "Enqueue Command" */ -+struct qm_eqcr_entry { -+ u8 __dont_write_directly__verb; -+ u8 dca; -+ u16 seqnum; -+ u32 orp; /* 24-bit */ -+ u32 fqid; /* 24-bit */ -+ u32 tag; -+ struct qm_fd fd; -+ u8 __reserved3[32]; -+} __packed; -+#define QM_EQCR_VERB_VBIT 0x80 -+#define QM_EQCR_VERB_CMD_MASK 0x61 /* but only one value; */ -+#define QM_EQCR_VERB_CMD_ENQUEUE 0x01 -+#define QM_EQCR_VERB_COLOUR_MASK 0x18 /* 4 possible values; */ -+#define QM_EQCR_VERB_COLOUR_GREEN 0x00 -+#define QM_EQCR_VERB_COLOUR_YELLOW 0x08 -+#define QM_EQCR_VERB_COLOUR_RED 0x10 -+#define QM_EQCR_VERB_COLOUR_OVERRIDE 0x18 -+#define QM_EQCR_VERB_INTERRUPT 0x04 /* on command consumption */ -+#define QM_EQCR_VERB_ORP 0x02 /* enable order restoration */ -+#define QM_EQCR_DCA_ENABLE 0x80 -+#define QM_EQCR_DCA_PARK 0x40 -+#define QM_EQCR_DCA_IDXMASK 0x0f /* "DQRR::idx" goes here */ -+#define QM_EQCR_SEQNUM_NESN 0x8000 /* Advance NESN */ -+#define QM_EQCR_SEQNUM_NLIS 0x4000 /* More fragments to come */ -+#define QM_EQCR_SEQNUM_SEQMASK 0x3fff /* sequence number goes here */ -+#define QM_EQCR_FQID_NULL 0 /* eg. for an ORP seqnum hole */ -+ -+/* See 1.5.8.2: "Frame Dequeue Response" */ -+struct qm_dqrr_entry { -+ u8 verb; -+ u8 stat; -+ u16 seqnum; /* 15-bit */ -+ u8 tok; -+ u8 __reserved2[3]; -+ u32 fqid; /* 24-bit */ -+ u32 contextB; -+ struct qm_fd fd; -+ u8 __reserved4[32]; -+}; -+#define QM_DQRR_VERB_VBIT 0x80 -+#define QM_DQRR_VERB_MASK 0x7f /* where the verb contains; */ -+#define QM_DQRR_VERB_FRAME_DEQUEUE 0x60 /* "this format" */ -+#define QM_DQRR_STAT_FQ_EMPTY 0x80 /* FQ empty */ -+#define QM_DQRR_STAT_FQ_HELDACTIVE 0x40 /* FQ held active */ -+#define QM_DQRR_STAT_FQ_FORCEELIGIBLE 0x20 /* FQ was force-eligible'd */ -+#define QM_DQRR_STAT_FD_VALID 0x10 /* has a non-NULL FD */ -+#define QM_DQRR_STAT_UNSCHEDULED 0x02 /* Unscheduled dequeue */ -+#define QM_DQRR_STAT_DQCR_EXPIRED 0x01 /* VDQCR or PDQCR expired*/ -+ -+/* See 1.5.8.3: "ERN Message Response" */ -+/* See 1.5.8.4: "FQ State Change Notification" */ -+struct qm_mr_entry { -+ u8 verb; -+ union { -+ struct { -+ u8 dca; -+ u16 seqnum; -+ u8 rc; /* Rejection Code */ -+ u32 orp:24; -+ u32 fqid; /* 24-bit */ -+ u32 tag; -+ struct qm_fd fd; -+ } __packed ern; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 colour:2; /* See QM_MR_DCERN_COLOUR_* */ -+ u8 __reserved1:3; -+ enum qm_dc_portal portal:3; -+#else -+ enum qm_dc_portal portal:3; -+ u8 __reserved1:3; -+ u8 colour:2; /* See QM_MR_DCERN_COLOUR_* */ -+#endif -+ u16 __reserved2; -+ u8 rc; /* Rejection Code */ -+ u32 __reserved3:24; -+ u32 fqid; /* 24-bit */ -+ u32 tag; -+ struct qm_fd fd; -+ } __packed dcern; -+ struct { -+ u8 fqs; /* Frame Queue Status */ -+ u8 __reserved1[6]; -+ u32 fqid; /* 24-bit */ -+ u32 contextB; -+ u8 __reserved2[16]; -+ } __packed fq; /* FQRN/FQRNI/FQRL/FQPN */ -+ }; -+ u8 __reserved2[32]; -+} __packed; -+#define QM_MR_VERB_VBIT 0x80 -+/* The "ern" VERB bits match QM_EQCR_VERB_*** so aren't reproduced here. ERNs -+ * originating from direct-connect portals ("dcern") use 0x20 as a verb which -+ * would be invalid as a s/w enqueue verb. A s/w ERN can be distinguished from -+ * the other MR types by noting if the 0x20 bit is unset. */ -+#define QM_MR_VERB_TYPE_MASK 0x27 -+#define QM_MR_VERB_DC_ERN 0x20 -+#define QM_MR_VERB_FQRN 0x21 -+#define QM_MR_VERB_FQRNI 0x22 -+#define QM_MR_VERB_FQRL 0x23 -+#define QM_MR_VERB_FQPN 0x24 -+#define QM_MR_RC_MASK 0xf0 /* contains one of; */ -+#define QM_MR_RC_CGR_TAILDROP 0x00 -+#define QM_MR_RC_WRED 0x10 -+#define QM_MR_RC_ERROR 0x20 -+#define QM_MR_RC_ORPWINDOW_EARLY 0x30 -+#define QM_MR_RC_ORPWINDOW_LATE 0x40 -+#define QM_MR_RC_FQ_TAILDROP 0x50 -+#define QM_MR_RC_ORPWINDOW_RETIRED 0x60 -+#define QM_MR_RC_ORP_ZERO 0x70 -+#define QM_MR_FQS_ORLPRESENT 0x02 /* ORL fragments to come */ -+#define QM_MR_FQS_NOTEMPTY 0x01 /* FQ has enqueued frames */ -+#define QM_MR_DCERN_COLOUR_GREEN 0x00 -+#define QM_MR_DCERN_COLOUR_YELLOW 0x01 -+#define QM_MR_DCERN_COLOUR_RED 0x02 -+#define QM_MR_DCERN_COLOUR_OVERRIDE 0x03 -+ -+/* An identical structure of FQD fields is present in the "Init FQ" command and -+ * the "Query FQ" result, it's suctioned out into the "struct qm_fqd" type. -+ * Within that, the 'stashing' and 'taildrop' pieces are also factored out, the -+ * latter has two inlines to assist with converting to/from the mant+exp -+ * representation. */ -+struct qm_fqd_stashing { -+ /* See QM_STASHING_EXCL_<...> */ -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 exclusive; -+ u8 __reserved1:2; -+ /* Numbers of cachelines */ -+ u8 annotation_cl:2; -+ u8 data_cl:2; -+ u8 context_cl:2; -+#else -+ u8 context_cl:2; -+ u8 data_cl:2; -+ u8 annotation_cl:2; -+ u8 __reserved1:2; -+ u8 exclusive; -+#endif -+} __packed; -+struct qm_fqd_taildrop { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u16 __reserved1:3; -+ u16 mant:8; -+ u16 exp:5; -+#else -+ u16 exp:5; -+ u16 mant:8; -+ u16 __reserved1:3; -+#endif -+} __packed; -+struct qm_fqd_oac { -+ /* See QM_OAC_<...> */ -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 oac:2; /* "Overhead Accounting Control" */ -+ u8 __reserved1:6; -+#else -+ u8 __reserved1:6; -+ u8 oac:2; /* "Overhead Accounting Control" */ -+#endif -+ /* Two's-complement value (-128 to +127) */ -+ signed char oal; /* "Overhead Accounting Length" */ -+} __packed; -+struct qm_fqd { -+ union { -+ u8 orpc; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 __reserved1:2; -+ u8 orprws:3; -+ u8 oa:1; -+ u8 olws:2; -+#else -+ u8 olws:2; -+ u8 oa:1; -+ u8 orprws:3; -+ u8 __reserved1:2; -+#endif -+ } __packed; -+ }; -+ u8 cgid; -+ u16 fq_ctrl; /* See QM_FQCTRL_<...> */ -+ union { -+ u16 dest_wq; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u16 channel:13; /* qm_channel */ -+ u16 wq:3; -+#else -+ u16 wq:3; -+ u16 channel:13; /* qm_channel */ -+#endif -+ } __packed dest; -+ }; -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u16 __reserved2:1; -+ u16 ics_cred:15; -+#else -+ u16 __reserved2:1; -+ u16 ics_cred:15; -+#endif -+ /* For "Initialize Frame Queue" commands, the write-enable mask -+ * determines whether 'td' or 'oac_init' is observed. For query -+ * commands, this field is always 'td', and 'oac_query' (below) reflects -+ * the Overhead ACcounting values. */ -+ union { -+ struct qm_fqd_taildrop td; -+ struct qm_fqd_oac oac_init; -+ }; -+ u32 context_b; -+ union { -+ /* Treat it as 64-bit opaque */ -+ u64 opaque; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 hi; -+ u32 lo; -+#else -+ u32 lo; -+ u32 hi; -+#endif -+ }; -+ /* Treat it as s/w portal stashing config */ -+ /* See 1.5.6.7.1: "FQD Context_A field used for [...] */ -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ struct qm_fqd_stashing stashing; -+ /* 48-bit address of FQ context to -+ * stash, must be cacheline-aligned */ -+ u16 context_hi; -+ u32 context_lo; -+#else -+ u32 context_lo; -+ u16 context_hi; -+ struct qm_fqd_stashing stashing; -+#endif -+ } __packed; -+ } context_a; -+ struct qm_fqd_oac oac_query; -+} __packed; -+/* 64-bit converters for context_hi/lo */ -+static inline u64 qm_fqd_stashing_get64(const struct qm_fqd *fqd) -+{ -+ return ((u64)fqd->context_a.context_hi << 32) | -+ (u64)fqd->context_a.context_lo; -+} -+static inline dma_addr_t qm_fqd_stashing_addr(const struct qm_fqd *fqd) -+{ -+ return (dma_addr_t)qm_fqd_stashing_get64(fqd); -+} -+static inline u64 qm_fqd_context_a_get64(const struct qm_fqd *fqd) -+{ -+ return ((u64)fqd->context_a.hi << 32) | -+ (u64)fqd->context_a.lo; -+} -+/* Macro, so we compile better when 'v' isn't necessarily 64-bit */ -+#define qm_fqd_stashing_set64(fqd, v) \ -+ do { \ -+ struct qm_fqd *__fqd931 = (fqd); \ -+ __fqd931->context_a.context_hi = upper_32_bits(v); \ -+ __fqd931->context_a.context_lo = lower_32_bits(v); \ -+ } while (0) -+#define qm_fqd_context_a_set64(fqd, v) \ -+ do { \ -+ struct qm_fqd *__fqd931 = (fqd); \ -+ __fqd931->context_a.hi = upper_32_bits(v); \ -+ __fqd931->context_a.lo = lower_32_bits(v); \ -+ } while (0) -+/* convert a threshold value into mant+exp representation */ -+static inline int qm_fqd_taildrop_set(struct qm_fqd_taildrop *td, u32 val, -+ int roundup) -+{ -+ u32 e = 0; -+ int oddbit = 0; -+ if (val > 0xe0000000) -+ return -ERANGE; -+ while (val > 0xff) { -+ oddbit = val & 1; -+ val >>= 1; -+ e++; -+ if (roundup && oddbit) -+ val++; -+ } -+ td->exp = e; -+ td->mant = val; -+ return 0; -+} -+/* and the other direction */ -+static inline u32 qm_fqd_taildrop_get(const struct qm_fqd_taildrop *td) -+{ -+ return (u32)td->mant << td->exp; -+} -+ -+/* See 1.5.2.2: "Frame Queue Descriptor (FQD)" */ -+/* Frame Queue Descriptor (FQD) field 'fq_ctrl' uses these constants */ -+#define QM_FQCTRL_MASK 0x07ff /* 'fq_ctrl' flags; */ -+#define QM_FQCTRL_CGE 0x0400 /* Congestion Group Enable */ -+#define QM_FQCTRL_TDE 0x0200 /* Tail-Drop Enable */ -+#define QM_FQCTRL_ORP 0x0100 /* ORP Enable */ -+#define QM_FQCTRL_CTXASTASHING 0x0080 /* Context-A stashing */ -+#define QM_FQCTRL_CPCSTASH 0x0040 /* CPC Stash Enable */ -+#define QM_FQCTRL_FORCESFDR 0x0008 /* High-priority SFDRs */ -+#define QM_FQCTRL_AVOIDBLOCK 0x0004 /* Don't block active */ -+#define QM_FQCTRL_HOLDACTIVE 0x0002 /* Hold active in portal */ -+#define QM_FQCTRL_PREFERINCACHE 0x0001 /* Aggressively cache FQD */ -+#define QM_FQCTRL_LOCKINCACHE QM_FQCTRL_PREFERINCACHE /* older naming */ -+ -+/* See 1.5.6.7.1: "FQD Context_A field used for [...] */ -+/* Frame Queue Descriptor (FQD) field 'CONTEXT_A' uses these constants */ -+#define QM_STASHING_EXCL_ANNOTATION 0x04 -+#define QM_STASHING_EXCL_DATA 0x02 -+#define QM_STASHING_EXCL_CTX 0x01 -+ -+/* See 1.5.5.3: "Intra Class Scheduling" */ -+/* FQD field 'OAC' (Overhead ACcounting) uses these constants */ -+#define QM_OAC_ICS 0x2 /* Accounting for Intra-Class Scheduling */ -+#define QM_OAC_CG 0x1 /* Accounting for Congestion Groups */ -+ -+/* See 1.5.8.4: "FQ State Change Notification" */ -+/* This struct represents the 32-bit "WR_PARM_[GYR]" parameters in CGR fields -+ * and associated commands/responses. The WRED parameters are calculated from -+ * these fields as follows; -+ * MaxTH = MA * (2 ^ Mn) -+ * Slope = SA / (2 ^ Sn) -+ * MaxP = 4 * (Pn + 1) -+ */ -+struct qm_cgr_wr_parm { -+ union { -+ u32 word; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 MA:8; -+ u32 Mn:5; -+ u32 SA:7; /* must be between 64-127 */ -+ u32 Sn:6; -+ u32 Pn:6; -+#else -+ u32 Pn:6; -+ u32 Sn:6; -+ u32 SA:7; /* must be between 64-127 */ -+ u32 Mn:5; -+ u32 MA:8; -+#endif -+ } __packed; -+ }; -+} __packed; -+/* This struct represents the 13-bit "CS_THRES" CGR field. In the corresponding -+ * management commands, this is padded to a 16-bit structure field, so that's -+ * how we represent it here. The congestion state threshold is calculated from -+ * these fields as follows; -+ * CS threshold = TA * (2 ^ Tn) -+ */ -+struct qm_cgr_cs_thres { -+ union { -+ u16 hword; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u16 __reserved:3; -+ u16 TA:8; -+ u16 Tn:5; -+#else -+ u16 Tn:5; -+ u16 TA:8; -+ u16 __reserved:3; -+#endif -+ } __packed; -+ }; -+} __packed; -+/* This identical structure of CGR fields is present in the "Init/Modify CGR" -+ * commands and the "Query CGR" result. It's suctioned out here into its own -+ * struct. */ -+struct __qm_mc_cgr { -+ struct qm_cgr_wr_parm wr_parm_g; -+ struct qm_cgr_wr_parm wr_parm_y; -+ struct qm_cgr_wr_parm wr_parm_r; -+ u8 wr_en_g; /* boolean, use QM_CGR_EN */ -+ u8 wr_en_y; /* boolean, use QM_CGR_EN */ -+ u8 wr_en_r; /* boolean, use QM_CGR_EN */ -+ u8 cscn_en; /* boolean, use QM_CGR_EN */ -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u16 cscn_targ_upd_ctrl; /* use QM_CSCN_TARG_UDP_ */ -+ u16 cscn_targ_dcp_low; /* CSCN_TARG_DCP low-16bits */ -+#else -+ u16 cscn_targ_dcp_low; /* CSCN_TARG_DCP low-16bits */ -+ u16 cscn_targ_upd_ctrl; /* use QM_CSCN_TARG_UDP_ */ -+#endif -+ }; -+ u32 cscn_targ; /* use QM_CGR_TARG_* */ -+ }; -+ u8 cstd_en; /* boolean, use QM_CGR_EN */ -+ u8 cs; /* boolean, only used in query response */ -+ union { -+ /* use qm_cgr_cs_thres_set64() */ -+ struct qm_cgr_cs_thres cs_thres; -+ u16 __cs_thres; -+ }; -+ u8 mode; /* QMAN_CGR_MODE_FRAME not supported in rev1.0 */ -+} __packed; -+#define QM_CGR_EN 0x01 /* For wr_en_*, cscn_en, cstd_en */ -+#define QM_CGR_TARG_UDP_CTRL_WRITE_BIT 0x8000 /* value written to portal bit*/ -+#define QM_CGR_TARG_UDP_CTRL_DCP 0x4000 /* 0: SWP, 1: DCP */ -+#define QM_CGR_TARG_PORTAL(n) (0x80000000 >> (n)) /* s/w portal, 0-9 */ -+#define QM_CGR_TARG_FMAN0 0x00200000 /* direct-connect portal: fman0 */ -+#define QM_CGR_TARG_FMAN1 0x00100000 /* : fman1 */ -+/* Convert CGR thresholds to/from "cs_thres" format */ -+static inline u64 qm_cgr_cs_thres_get64(const struct qm_cgr_cs_thres *th) -+{ -+ return (u64)th->TA << th->Tn; -+} -+static inline int qm_cgr_cs_thres_set64(struct qm_cgr_cs_thres *th, u64 val, -+ int roundup) -+{ -+ u32 e = 0; -+ int oddbit = 0; -+ while (val > 0xff) { -+ oddbit = val & 1; -+ val >>= 1; -+ e++; -+ if (roundup && oddbit) -+ val++; -+ } -+ th->Tn = e; -+ th->TA = val; -+ return 0; -+} -+ -+/* See 1.5.8.5.1: "Initialize FQ" */ -+/* See 1.5.8.5.2: "Query FQ" */ -+/* See 1.5.8.5.3: "Query FQ Non-Programmable Fields" */ -+/* See 1.5.8.5.4: "Alter FQ State Commands " */ -+/* See 1.5.8.6.1: "Initialize/Modify CGR" */ -+/* See 1.5.8.6.2: "CGR Test Write" */ -+/* See 1.5.8.6.3: "Query CGR" */ -+/* See 1.5.8.6.4: "Query Congestion Group State" */ -+struct qm_mcc_initfq { -+ u8 __reserved1; -+ u16 we_mask; /* Write Enable Mask */ -+ u32 fqid; /* 24-bit */ -+ u16 count; /* Initialises 'count+1' FQDs */ -+ struct qm_fqd fqd; /* the FQD fields go here */ -+ u8 __reserved3[30]; -+} __packed; -+struct qm_mcc_queryfq { -+ u8 __reserved1[3]; -+ u32 fqid; /* 24-bit */ -+ u8 __reserved2[56]; -+} __packed; -+struct qm_mcc_queryfq_np { -+ u8 __reserved1[3]; -+ u32 fqid; /* 24-bit */ -+ u8 __reserved2[56]; -+} __packed; -+struct qm_mcc_alterfq { -+ u8 __reserved1[3]; -+ u32 fqid; /* 24-bit */ -+ u8 __reserved2; -+ u8 count; /* number of consecutive FQID */ -+ u8 __reserved3[10]; -+ u32 context_b; /* frame queue context b */ -+ u8 __reserved4[40]; -+} __packed; -+struct qm_mcc_initcgr { -+ u8 __reserved1; -+ u16 we_mask; /* Write Enable Mask */ -+ struct __qm_mc_cgr cgr; /* CGR fields */ -+ u8 __reserved2[2]; -+ u8 cgid; -+ u8 __reserved4[32]; -+} __packed; -+struct qm_mcc_cgrtestwrite { -+ u8 __reserved1[2]; -+ u8 i_bcnt_hi:8;/* high 8-bits of 40-bit "Instant" */ -+ u32 i_bcnt_lo; /* low 32-bits of 40-bit */ -+ u8 __reserved2[23]; -+ u8 cgid; -+ u8 __reserved3[32]; -+} __packed; -+struct qm_mcc_querycgr { -+ u8 __reserved1[30]; -+ u8 cgid; -+ u8 __reserved2[32]; -+} __packed; -+struct qm_mcc_querycongestion { -+ u8 __reserved[63]; -+} __packed; -+struct qm_mcc_querywq { -+ u8 __reserved; -+ /* select channel if verb != QUERYWQ_DEDICATED */ -+ union { -+ u16 channel_wq; /* ignores wq (3 lsbits) */ -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u16 id:13; /* qm_channel */ -+ u16 __reserved1:3; -+#else -+ u16 __reserved1:3; -+ u16 id:13; /* qm_channel */ -+#endif -+ } __packed channel; -+ }; -+ u8 __reserved2[60]; -+} __packed; -+ -+struct qm_mcc_ceetm_lfqmt_config { -+ u8 __reserved1[4]; -+ u32 lfqid:24; -+ u8 __reserved2[2]; -+ u16 cqid; -+ u8 __reserved3[2]; -+ u16 dctidx; -+ u8 __reserved4[48]; -+} __packed; -+ -+struct qm_mcc_ceetm_lfqmt_query { -+ u8 __reserved1[4]; -+ u32 lfqid:24; -+ u8 __reserved2[56]; -+} __packed; -+ -+struct qm_mcc_ceetm_cq_config { -+ u8 __reserved1; -+ u16 cqid; -+ u8 dcpid; -+ u8 __reserved2; -+ u16 ccgid; -+ u8 __reserved3[56]; -+} __packed; -+ -+struct qm_mcc_ceetm_cq_query { -+ u8 __reserved1; -+ u16 cqid; -+ u8 dcpid; -+ u8 __reserved2[59]; -+} __packed; -+ -+struct qm_mcc_ceetm_dct_config { -+ u8 __reserved1; -+ u16 dctidx; -+ u8 dcpid; -+ u8 __reserved2[15]; -+ u32 context_b; -+ u64 context_a; -+ u8 __reserved3[32]; -+} __packed; -+ -+struct qm_mcc_ceetm_dct_query { -+ u8 __reserved1; -+ u16 dctidx; -+ u8 dcpid; -+ u8 __reserved2[59]; -+} __packed; -+ -+struct qm_mcc_ceetm_class_scheduler_config { -+ u8 __reserved1; -+ u16 cqcid; -+ u8 dcpid; -+ u8 __reserved2[6]; -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 gpc_reserved:1; -+ u8 gpc_combine_flag:1; -+ u8 gpc_prio_b:3; -+ u8 gpc_prio_a:3; -+#else -+ u8 gpc_prio_a:3; -+ u8 gpc_prio_b:3; -+ u8 gpc_combine_flag:1; -+ u8 gpc_reserved:1; -+#endif -+ u16 crem; -+ u16 erem; -+ u8 w[8]; -+ u8 __reserved3[40]; -+} __packed; -+ -+struct qm_mcc_ceetm_class_scheduler_query { -+ u8 __reserved1; -+ u16 cqcid; -+ u8 dcpid; -+ u8 __reserved2[59]; -+} __packed; -+ -+#define CEETM_COMMAND_CHANNEL_MAPPING (0 << 12) -+#define CEETM_COMMAND_SP_MAPPING (1 << 12) -+#define CEETM_COMMAND_CHANNEL_SHAPER (2 << 12) -+#define CEETM_COMMAND_LNI_SHAPER (3 << 12) -+#define CEETM_COMMAND_TCFC (4 << 12) -+ -+#define CEETM_CCGRID_MASK 0x01FF -+#define CEETM_CCGR_CM_CONFIGURE (0 << 14) -+#define CEETM_CCGR_DN_CONFIGURE (1 << 14) -+#define CEETM_CCGR_TEST_WRITE (2 << 14) -+#define CEETM_CCGR_CM_QUERY (0 << 14) -+#define CEETM_CCGR_DN_QUERY (1 << 14) -+#define CEETM_CCGR_DN_QUERY_FLUSH (2 << 14) -+#define CEETM_QUERY_CONGESTION_STATE (3 << 14) -+ -+struct qm_mcc_ceetm_mapping_shaper_tcfc_config { -+ u8 __reserved1; -+ u16 cid; -+ u8 dcpid; -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 map_shaped:1; -+ u8 map_reserved:4; -+ u8 map_lni_id:3; -+#else -+ u8 map_lni_id:3; -+ u8 map_reserved:4; -+ u8 map_shaped:1; -+#endif -+ u8 __reserved2[58]; -+ } __packed channel_mapping; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 map_reserved:5; -+ u8 map_lni_id:3; -+#else -+ u8 map_lni_id:3; -+ u8 map_reserved:5; -+#endif -+ u8 __reserved2[58]; -+ } __packed sp_mapping; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 cpl:1; -+ u8 cpl_reserved:2; -+ u8 oal:5; -+#else -+ u8 oal:5; -+ u8 cpl_reserved:2; -+ u8 cpl:1; -+#endif -+ u32 crtcr:24; -+ u32 ertcr:24; -+ u16 crtbl; -+ u16 ertbl; -+ u8 mps; /* This will be hardcoded by driver with 60 */ -+ u8 __reserved2[47]; -+ } __packed shaper_config; -+ struct { -+ u8 __reserved2[11]; -+ u64 lnitcfcc; -+ u8 __reserved3[40]; -+ } __packed tcfc_config; -+ }; -+} __packed; -+ -+struct qm_mcc_ceetm_mapping_shaper_tcfc_query { -+ u8 __reserved1; -+ u16 cid; -+ u8 dcpid; -+ u8 __reserved2[59]; -+} __packed; -+ -+struct qm_mcc_ceetm_ccgr_config { -+ u8 __reserved1; -+ u16 ccgrid; -+ u8 dcpid; -+ u8 __reserved2; -+ u16 we_mask; -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 ctl_reserved:1; -+ u8 ctl_wr_en_g:1; -+ u8 ctl_wr_en_y:1; -+ u8 ctl_wr_en_r:1; -+ u8 ctl_td_en:1; -+ u8 ctl_td_mode:1; -+ u8 ctl_cscn_en:1; -+ u8 ctl_mode:1; -+#else -+ u8 ctl_mode:1; -+ u8 ctl_cscn_en:1; -+ u8 ctl_td_mode:1; -+ u8 ctl_td_en:1; -+ u8 ctl_wr_en_r:1; -+ u8 ctl_wr_en_y:1; -+ u8 ctl_wr_en_g:1; -+ u8 ctl_reserved:1; -+#endif -+ u8 cdv; -+ u16 cscn_tupd; -+ u8 oal; -+ u8 __reserved3; -+ struct qm_cgr_cs_thres cs_thres; -+ struct qm_cgr_cs_thres cs_thres_x; -+ struct qm_cgr_cs_thres td_thres; -+ struct qm_cgr_wr_parm wr_parm_g; -+ struct qm_cgr_wr_parm wr_parm_y; -+ struct qm_cgr_wr_parm wr_parm_r; -+ } __packed cm_config; -+ struct { -+ u8 dnc; -+ u8 dn0; -+ u8 dn1; -+ u64 dnba:40; -+ u8 __reserved3[2]; -+ u16 dnth_0; -+ u8 __reserved4[2]; -+ u16 dnth_1; -+ u8 __reserved5[8]; -+ } __packed dn_config; -+ struct { -+ u8 __reserved3[3]; -+ u64 i_cnt:40; -+ u8 __reserved4[16]; -+ } __packed test_write; -+ }; -+ u8 __reserved5[32]; -+} __packed; -+ -+struct qm_mcc_ceetm_ccgr_query { -+ u8 __reserved1; -+ u16 ccgrid; -+ u8 dcpid; -+ u8 __reserved2[59]; -+} __packed; -+ -+struct qm_mcc_ceetm_cq_peek_pop_xsfdrread { -+ u8 __reserved1; -+ u16 cqid; -+ u8 dcpid; -+ u8 ct; -+ u16 xsfdr; -+ u8 __reserved2[56]; -+} __packed; -+ -+#define CEETM_QUERY_DEQUEUE_STATISTICS 0x00 -+#define CEETM_QUERY_DEQUEUE_CLEAR_STATISTICS 0x01 -+#define CEETM_WRITE_DEQUEUE_STATISTICS 0x02 -+#define CEETM_QUERY_REJECT_STATISTICS 0x03 -+#define CEETM_QUERY_REJECT_CLEAR_STATISTICS 0x04 -+#define CEETM_WRITE_REJECT_STATISTICS 0x05 -+struct qm_mcc_ceetm_statistics_query_write { -+ u8 __reserved1; -+ u16 cid; -+ u8 dcpid; -+ u8 ct; -+ u8 __reserved2[13]; -+ u64 frm_cnt:40; -+ u8 __reserved3[2]; -+ u64 byte_cnt:48; -+ u8 __reserved[32]; -+} __packed; -+ -+struct qm_mc_command { -+ u8 __dont_write_directly__verb; -+ union { -+ struct qm_mcc_initfq initfq; -+ struct qm_mcc_queryfq queryfq; -+ struct qm_mcc_queryfq_np queryfq_np; -+ struct qm_mcc_alterfq alterfq; -+ struct qm_mcc_initcgr initcgr; -+ struct qm_mcc_cgrtestwrite cgrtestwrite; -+ struct qm_mcc_querycgr querycgr; -+ struct qm_mcc_querycongestion querycongestion; -+ struct qm_mcc_querywq querywq; -+ struct qm_mcc_ceetm_lfqmt_config lfqmt_config; -+ struct qm_mcc_ceetm_lfqmt_query lfqmt_query; -+ struct qm_mcc_ceetm_cq_config cq_config; -+ struct qm_mcc_ceetm_cq_query cq_query; -+ struct qm_mcc_ceetm_dct_config dct_config; -+ struct qm_mcc_ceetm_dct_query dct_query; -+ struct qm_mcc_ceetm_class_scheduler_config csch_config; -+ struct qm_mcc_ceetm_class_scheduler_query csch_query; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_config mst_config; -+ struct qm_mcc_ceetm_mapping_shaper_tcfc_query mst_query; -+ struct qm_mcc_ceetm_ccgr_config ccgr_config; -+ struct qm_mcc_ceetm_ccgr_query ccgr_query; -+ struct qm_mcc_ceetm_cq_peek_pop_xsfdrread cq_ppxr; -+ struct qm_mcc_ceetm_statistics_query_write stats_query_write; -+ }; -+} __packed; -+#define QM_MCC_VERB_VBIT 0x80 -+#define QM_MCC_VERB_MASK 0x7f /* where the verb contains; */ -+#define QM_MCC_VERB_INITFQ_PARKED 0x40 -+#define QM_MCC_VERB_INITFQ_SCHED 0x41 -+#define QM_MCC_VERB_QUERYFQ 0x44 -+#define QM_MCC_VERB_QUERYFQ_NP 0x45 /* "non-programmable" fields */ -+#define QM_MCC_VERB_QUERYWQ 0x46 -+#define QM_MCC_VERB_QUERYWQ_DEDICATED 0x47 -+#define QM_MCC_VERB_ALTER_SCHED 0x48 /* Schedule FQ */ -+#define QM_MCC_VERB_ALTER_FE 0x49 /* Force Eligible FQ */ -+#define QM_MCC_VERB_ALTER_RETIRE 0x4a /* Retire FQ */ -+#define QM_MCC_VERB_ALTER_OOS 0x4b /* Take FQ out of service */ -+#define QM_MCC_VERB_ALTER_FQXON 0x4d /* FQ XON */ -+#define QM_MCC_VERB_ALTER_FQXOFF 0x4e /* FQ XOFF */ -+#define QM_MCC_VERB_INITCGR 0x50 -+#define QM_MCC_VERB_MODIFYCGR 0x51 -+#define QM_MCC_VERB_CGRTESTWRITE 0x52 -+#define QM_MCC_VERB_QUERYCGR 0x58 -+#define QM_MCC_VERB_QUERYCONGESTION 0x59 -+/* INITFQ-specific flags */ -+#define QM_INITFQ_WE_MASK 0x01ff /* 'Write Enable' flags; */ -+#define QM_INITFQ_WE_OAC 0x0100 -+#define QM_INITFQ_WE_ORPC 0x0080 -+#define QM_INITFQ_WE_CGID 0x0040 -+#define QM_INITFQ_WE_FQCTRL 0x0020 -+#define QM_INITFQ_WE_DESTWQ 0x0010 -+#define QM_INITFQ_WE_ICSCRED 0x0008 -+#define QM_INITFQ_WE_TDTHRESH 0x0004 -+#define QM_INITFQ_WE_CONTEXTB 0x0002 -+#define QM_INITFQ_WE_CONTEXTA 0x0001 -+/* INITCGR/MODIFYCGR-specific flags */ -+#define QM_CGR_WE_MASK 0x07ff /* 'Write Enable Mask'; */ -+#define QM_CGR_WE_WR_PARM_G 0x0400 -+#define QM_CGR_WE_WR_PARM_Y 0x0200 -+#define QM_CGR_WE_WR_PARM_R 0x0100 -+#define QM_CGR_WE_WR_EN_G 0x0080 -+#define QM_CGR_WE_WR_EN_Y 0x0040 -+#define QM_CGR_WE_WR_EN_R 0x0020 -+#define QM_CGR_WE_CSCN_EN 0x0010 -+#define QM_CGR_WE_CSCN_TARG 0x0008 -+#define QM_CGR_WE_CSTD_EN 0x0004 -+#define QM_CGR_WE_CS_THRES 0x0002 -+#define QM_CGR_WE_MODE 0x0001 -+ -+/* See 1.5.9.7 CEETM Management Commands */ -+#define QM_CEETM_VERB_LFQMT_CONFIG 0x70 -+#define QM_CEETM_VERB_LFQMT_QUERY 0x71 -+#define QM_CEETM_VERB_CQ_CONFIG 0x72 -+#define QM_CEETM_VERB_CQ_QUERY 0x73 -+#define QM_CEETM_VERB_DCT_CONFIG 0x74 -+#define QM_CEETM_VERB_DCT_QUERY 0x75 -+#define QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG 0x76 -+#define QM_CEETM_VERB_CLASS_SCHEDULER_QUERY 0x77 -+#define QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG 0x78 -+#define QM_CEETM_VERB_MAPPING_SHAPER_TCFC_QUERY 0x79 -+#define QM_CEETM_VERB_CCGR_CONFIG 0x7A -+#define QM_CEETM_VERB_CCGR_QUERY 0x7B -+#define QM_CEETM_VERB_CQ_PEEK_POP_XFDRREAD 0x7C -+#define QM_CEETM_VERB_STATISTICS_QUERY_WRITE 0x7D -+ -+/* See 1.5.8.5.1: "Initialize FQ" */ -+/* See 1.5.8.5.2: "Query FQ" */ -+/* See 1.5.8.5.3: "Query FQ Non-Programmable Fields" */ -+/* See 1.5.8.5.4: "Alter FQ State Commands " */ -+/* See 1.5.8.6.1: "Initialize/Modify CGR" */ -+/* See 1.5.8.6.2: "CGR Test Write" */ -+/* See 1.5.8.6.3: "Query CGR" */ -+/* See 1.5.8.6.4: "Query Congestion Group State" */ -+struct qm_mcr_initfq { -+ u8 __reserved1[62]; -+} __packed; -+struct qm_mcr_queryfq { -+ u8 __reserved1[8]; -+ struct qm_fqd fqd; /* the FQD fields are here */ -+ u8 __reserved2[30]; -+} __packed; -+struct qm_mcr_queryfq_np { -+ u8 __reserved1; -+ u8 state; /* QM_MCR_NP_STATE_*** */ -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 __reserved2; -+ u32 fqd_link:24; -+ u16 __reserved3:2; -+ u16 odp_seq:14; -+ u16 __reserved4:2; -+ u16 orp_nesn:14; -+ u16 __reserved5:1; -+ u16 orp_ea_hseq:15; -+ u16 __reserved6:1; -+ u16 orp_ea_tseq:15; -+ u8 __reserved7; -+ u32 orp_ea_hptr:24; -+ u8 __reserved8; -+ u32 orp_ea_tptr:24; -+ u8 __reserved9; -+ u32 pfdr_hptr:24; -+ u8 __reserved10; -+ u32 pfdr_tptr:24; -+ u8 __reserved11[5]; -+ u8 __reserved12:7; -+ u8 is:1; -+ u16 ics_surp; -+ u32 byte_cnt; -+ u8 __reserved13; -+ u32 frm_cnt:24; -+ u32 __reserved14; -+ u16 ra1_sfdr; /* QM_MCR_NP_RA1_*** */ -+ u16 ra2_sfdr; /* QM_MCR_NP_RA2_*** */ -+ u16 __reserved15; -+ u16 od1_sfdr; /* QM_MCR_NP_OD1_*** */ -+ u16 od2_sfdr; /* QM_MCR_NP_OD2_*** */ -+ u16 od3_sfdr; /* QM_MCR_NP_OD3_*** */ -+#else -+ u8 __reserved2; -+ u32 fqd_link:24; -+ -+ u16 odp_seq:14; -+ u16 __reserved3:2; -+ -+ u16 orp_nesn:14; -+ u16 __reserved4:2; -+ -+ u16 orp_ea_hseq:15; -+ u16 __reserved5:1; -+ -+ u16 orp_ea_tseq:15; -+ u16 __reserved6:1; -+ -+ u8 __reserved7; -+ u32 orp_ea_hptr:24; -+ -+ u8 __reserved8; -+ u32 orp_ea_tptr:24; -+ -+ u8 __reserved9; -+ u32 pfdr_hptr:24; -+ -+ u8 __reserved10; -+ u32 pfdr_tptr:24; -+ -+ u8 __reserved11[5]; -+ u8 is:1; -+ u8 __reserved12:7; -+ u16 ics_surp; -+ u32 byte_cnt; -+ u8 __reserved13; -+ u32 frm_cnt:24; -+ u32 __reserved14; -+ u16 ra1_sfdr; /* QM_MCR_NP_RA1_*** */ -+ u16 ra2_sfdr; /* QM_MCR_NP_RA2_*** */ -+ u16 __reserved15; -+ u16 od1_sfdr; /* QM_MCR_NP_OD1_*** */ -+ u16 od2_sfdr; /* QM_MCR_NP_OD2_*** */ -+ u16 od3_sfdr; /* QM_MCR_NP_OD3_*** */ -+#endif -+} __packed; -+ -+ -+struct qm_mcr_alterfq { -+ u8 fqs; /* Frame Queue Status */ -+ u8 __reserved1[61]; -+} __packed; -+struct qm_mcr_initcgr { -+ u8 __reserved1[62]; -+} __packed; -+struct qm_mcr_cgrtestwrite { -+ u16 __reserved1; -+ struct __qm_mc_cgr cgr; /* CGR fields */ -+ u8 __reserved2[3]; -+ u32 __reserved3:24; -+ u32 i_bcnt_hi:8;/* high 8-bits of 40-bit "Instant" */ -+ u32 i_bcnt_lo; /* low 32-bits of 40-bit */ -+ u32 __reserved4:24; -+ u32 a_bcnt_hi:8;/* high 8-bits of 40-bit "Average" */ -+ u32 a_bcnt_lo; /* low 32-bits of 40-bit */ -+ u16 lgt; /* Last Group Tick */ -+ u16 wr_prob_g; -+ u16 wr_prob_y; -+ u16 wr_prob_r; -+ u8 __reserved5[8]; -+} __packed; -+struct qm_mcr_querycgr { -+ u16 __reserved1; -+ struct __qm_mc_cgr cgr; /* CGR fields */ -+ u8 __reserved2[3]; -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 __reserved3:24; -+ u32 i_bcnt_hi:8;/* high 8-bits of 40-bit "Instant" */ -+ u32 i_bcnt_lo; /* low 32-bits of 40-bit */ -+#else -+ u32 i_bcnt_lo; /* low 32-bits of 40-bit */ -+ u32 i_bcnt_hi:8;/* high 8-bits of 40-bit "Instant" */ -+ u32 __reserved3:24; -+#endif -+ }; -+ u64 i_bcnt; -+ }; -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u32 __reserved4:24; -+ u32 a_bcnt_hi:8;/* high 8-bits of 40-bit "Average" */ -+ u32 a_bcnt_lo; /* low 32-bits of 40-bit */ -+#else -+ u32 a_bcnt_lo; /* low 32-bits of 40-bit */ -+ u32 a_bcnt_hi:8;/* high 8-bits of 40-bit "Average" */ -+ u32 __reserved4:24; -+#endif -+ }; -+ u64 a_bcnt; -+ }; -+ union { -+ u32 cscn_targ_swp[4]; -+ u8 __reserved5[16]; -+ }; -+} __packed; -+static inline u64 qm_mcr_querycgr_i_get64(const struct qm_mcr_querycgr *q) -+{ -+ return be64_to_cpu(q->i_bcnt); -+} -+static inline u64 qm_mcr_querycgr_a_get64(const struct qm_mcr_querycgr *q) -+{ -+ return be64_to_cpu(q->a_bcnt); -+} -+static inline u64 qm_mcr_cgrtestwrite_i_get64( -+ const struct qm_mcr_cgrtestwrite *q) -+{ -+ return be64_to_cpu(((u64)q->i_bcnt_hi << 32) | (u64)q->i_bcnt_lo); -+} -+static inline u64 qm_mcr_cgrtestwrite_a_get64( -+ const struct qm_mcr_cgrtestwrite *q) -+{ -+ return be64_to_cpu(((u64)q->a_bcnt_hi << 32) | (u64)q->a_bcnt_lo); -+} -+/* Macro, so we compile better if 'v' isn't always 64-bit */ -+#define qm_mcr_querycgr_i_set64(q, v) \ -+ do { \ -+ struct qm_mcr_querycgr *__q931 = (fd); \ -+ __q931->i_bcnt_hi = upper_32_bits(v); \ -+ __q931->i_bcnt_lo = lower_32_bits(v); \ -+ } while (0) -+#define qm_mcr_querycgr_a_set64(q, v) \ -+ do { \ -+ struct qm_mcr_querycgr *__q931 = (fd); \ -+ __q931->a_bcnt_hi = upper_32_bits(v); \ -+ __q931->a_bcnt_lo = lower_32_bits(v); \ -+ } while (0) -+struct __qm_mcr_querycongestion { -+ u32 __state[8]; -+}; -+struct qm_mcr_querycongestion { -+ u8 __reserved[30]; -+ /* Access this struct using QM_MCR_QUERYCONGESTION() */ -+ struct __qm_mcr_querycongestion state; -+} __packed; -+struct qm_mcr_querywq { -+ union { -+ u16 channel_wq; /* ignores wq (3 lsbits) */ -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u16 id:13; /* qm_channel */ -+ u16 __reserved:3; -+#else -+ u16 __reserved:3; -+ u16 id:13; /* qm_channel */ -+#endif -+ } __packed channel; -+ }; -+ u8 __reserved[28]; -+ u32 wq_len[8]; -+} __packed; -+ -+/* QMAN CEETM Management Command Response */ -+struct qm_mcr_ceetm_lfqmt_config { -+ u8 __reserved1[62]; -+} __packed; -+struct qm_mcr_ceetm_lfqmt_query { -+ u8 __reserved1[8]; -+ u16 cqid; -+ u8 __reserved2[2]; -+ u16 dctidx; -+ u8 __reserved3[2]; -+ u16 ccgid; -+ u8 __reserved4[44]; -+} __packed; -+ -+struct qm_mcr_ceetm_cq_config { -+ u8 __reserved1[62]; -+} __packed; -+ -+struct qm_mcr_ceetm_cq_query { -+ u8 __reserved1[4]; -+ u16 ccgid; -+ u16 state; -+ u32 pfdr_hptr:24; -+ u32 pfdr_tptr:24; -+ u16 od1_xsfdr; -+ u16 od2_xsfdr; -+ u16 od3_xsfdr; -+ u16 od4_xsfdr; -+ u16 od5_xsfdr; -+ u16 od6_xsfdr; -+ u16 ra1_xsfdr; -+ u16 ra2_xsfdr; -+ u8 __reserved2; -+ u32 frm_cnt:24; -+ u8 __reserved333[28]; -+} __packed; -+ -+struct qm_mcr_ceetm_dct_config { -+ u8 __reserved1[62]; -+} __packed; -+ -+struct qm_mcr_ceetm_dct_query { -+ u8 __reserved1[18]; -+ u32 context_b; -+ u64 context_a; -+ u8 __reserved2[32]; -+} __packed; -+ -+struct qm_mcr_ceetm_class_scheduler_config { -+ u8 __reserved1[62]; -+} __packed; -+ -+struct qm_mcr_ceetm_class_scheduler_query { -+ u8 __reserved1[9]; -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 gpc_reserved:1; -+ u8 gpc_combine_flag:1; -+ u8 gpc_prio_b:3; -+ u8 gpc_prio_a:3; -+#else -+ u8 gpc_prio_a:3; -+ u8 gpc_prio_b:3; -+ u8 gpc_combine_flag:1; -+ u8 gpc_reserved:1; -+#endif -+ u16 crem; -+ u16 erem; -+ u8 w[8]; -+ u8 __reserved2[5]; -+ u32 wbfslist:24; -+ u32 d8; -+ u32 d9; -+ u32 d10; -+ u32 d11; -+ u32 d12; -+ u32 d13; -+ u32 d14; -+ u32 d15; -+} __packed; -+ -+struct qm_mcr_ceetm_mapping_shaper_tcfc_config { -+ u16 cid; -+ u8 __reserved2[60]; -+} __packed; -+ -+struct qm_mcr_ceetm_mapping_shaper_tcfc_query { -+ u16 cid; -+ u8 __reserved1; -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 map_shaped:1; -+ u8 map_reserved:4; -+ u8 map_lni_id:3; -+#else -+ u8 map_lni_id:3; -+ u8 map_reserved:4; -+ u8 map_shaped:1; -+#endif -+ u8 __reserved2[58]; -+ } __packed channel_mapping_query; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 map_reserved:5; -+ u8 map_lni_id:3; -+#else -+ u8 map_lni_id:3; -+ u8 map_reserved:5; -+#endif -+ u8 __reserved2[58]; -+ } __packed sp_mapping_query; -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 cpl:1; -+ u8 cpl_reserved:2; -+ u8 oal:5; -+#else -+ u8 oal:5; -+ u8 cpl_reserved:2; -+ u8 cpl:1; -+#endif -+ u32 crtcr:24; -+ u32 ertcr:24; -+ u16 crtbl; -+ u16 ertbl; -+ u8 mps; -+ u8 __reserved2[15]; -+ u32 crat; -+ u32 erat; -+ u8 __reserved3[24]; -+ } __packed shaper_query; -+ struct { -+ u8 __reserved1[11]; -+ u64 lnitcfcc; -+ u8 __reserved3[40]; -+ } __packed tcfc_query; -+ }; -+} __packed; -+ -+struct qm_mcr_ceetm_ccgr_config { -+ u8 __reserved1[46]; -+ union { -+ u8 __reserved2[8]; -+ struct { -+ u16 timestamp; -+ u16 wr_porb_g; -+ u16 wr_prob_y; -+ u16 wr_prob_r; -+ } __packed test_write; -+ }; -+ u8 __reserved3[8]; -+} __packed; -+ -+struct qm_mcr_ceetm_ccgr_query { -+ u8 __reserved1[6]; -+ union { -+ struct { -+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -+ u8 ctl_reserved:1; -+ u8 ctl_wr_en_g:1; -+ u8 ctl_wr_en_y:1; -+ u8 ctl_wr_en_r:1; -+ u8 ctl_td_en:1; -+ u8 ctl_td_mode:1; -+ u8 ctl_cscn_en:1; -+ u8 ctl_mode:1; -+#else -+ u8 ctl_mode:1; -+ u8 ctl_cscn_en:1; -+ u8 ctl_td_mode:1; -+ u8 ctl_td_en:1; -+ u8 ctl_wr_en_r:1; -+ u8 ctl_wr_en_y:1; -+ u8 ctl_wr_en_g:1; -+ u8 ctl_reserved:1; -+#endif -+ u8 cdv; -+ u8 __reserved2[2]; -+ u8 oal; -+ u8 __reserved3; -+ struct qm_cgr_cs_thres cs_thres; -+ struct qm_cgr_cs_thres cs_thres_x; -+ struct qm_cgr_cs_thres td_thres; -+ struct qm_cgr_wr_parm wr_parm_g; -+ struct qm_cgr_wr_parm wr_parm_y; -+ struct qm_cgr_wr_parm wr_parm_r; -+ u16 cscn_targ_dcp; -+ u8 dcp_lsn; -+ u64 i_cnt:40; -+ u8 __reserved4[3]; -+ u64 a_cnt:40; -+ u32 cscn_targ_swp[4]; -+ } __packed cm_query; -+ struct { -+ u8 dnc; -+ u8 dn0; -+ u8 dn1; -+ u64 dnba:40; -+ u8 __reserved2[2]; -+ u16 dnth_0; -+ u8 __reserved3[2]; -+ u16 dnth_1; -+ u8 __reserved4[10]; -+ u16 dnacc_0; -+ u8 __reserved5[2]; -+ u16 dnacc_1; -+ u8 __reserved6[24]; -+ } __packed dn_query; -+ struct { -+ u8 __reserved2[24]; -+ struct __qm_mcr_querycongestion state; -+ } __packed congestion_state; -+ -+ }; -+} __packed; -+ -+struct qm_mcr_ceetm_cq_peek_pop_xsfdrread { -+ u8 stat; -+ u8 __reserved1[11]; -+ u16 dctidx; -+ struct qm_fd fd; -+ u8 __reserved2[32]; -+} __packed; -+ -+struct qm_mcr_ceetm_statistics_query { -+ u8 __reserved1[17]; -+ u64 frm_cnt:40; -+ u8 __reserved2[2]; -+ u64 byte_cnt:48; -+ u8 __reserved3[32]; -+} __packed; -+ -+struct qm_mc_result { -+ u8 verb; -+ u8 result; -+ union { -+ struct qm_mcr_initfq initfq; -+ struct qm_mcr_queryfq queryfq; -+ struct qm_mcr_queryfq_np queryfq_np; -+ struct qm_mcr_alterfq alterfq; -+ struct qm_mcr_initcgr initcgr; -+ struct qm_mcr_cgrtestwrite cgrtestwrite; -+ struct qm_mcr_querycgr querycgr; -+ struct qm_mcr_querycongestion querycongestion; -+ struct qm_mcr_querywq querywq; -+ struct qm_mcr_ceetm_lfqmt_config lfqmt_config; -+ struct qm_mcr_ceetm_lfqmt_query lfqmt_query; -+ struct qm_mcr_ceetm_cq_config cq_config; -+ struct qm_mcr_ceetm_cq_query cq_query; -+ struct qm_mcr_ceetm_dct_config dct_config; -+ struct qm_mcr_ceetm_dct_query dct_query; -+ struct qm_mcr_ceetm_class_scheduler_config csch_config; -+ struct qm_mcr_ceetm_class_scheduler_query csch_query; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_config mst_config; -+ struct qm_mcr_ceetm_mapping_shaper_tcfc_query mst_query; -+ struct qm_mcr_ceetm_ccgr_config ccgr_config; -+ struct qm_mcr_ceetm_ccgr_query ccgr_query; -+ struct qm_mcr_ceetm_cq_peek_pop_xsfdrread cq_ppxr; -+ struct qm_mcr_ceetm_statistics_query stats_query; -+ }; -+} __packed; -+ -+#define QM_MCR_VERB_RRID 0x80 -+#define QM_MCR_VERB_MASK QM_MCC_VERB_MASK -+#define QM_MCR_VERB_INITFQ_PARKED QM_MCC_VERB_INITFQ_PARKED -+#define QM_MCR_VERB_INITFQ_SCHED QM_MCC_VERB_INITFQ_SCHED -+#define QM_MCR_VERB_QUERYFQ QM_MCC_VERB_QUERYFQ -+#define QM_MCR_VERB_QUERYFQ_NP QM_MCC_VERB_QUERYFQ_NP -+#define QM_MCR_VERB_QUERYWQ QM_MCC_VERB_QUERYWQ -+#define QM_MCR_VERB_QUERYWQ_DEDICATED QM_MCC_VERB_QUERYWQ_DEDICATED -+#define QM_MCR_VERB_ALTER_SCHED QM_MCC_VERB_ALTER_SCHED -+#define QM_MCR_VERB_ALTER_FE QM_MCC_VERB_ALTER_FE -+#define QM_MCR_VERB_ALTER_RETIRE QM_MCC_VERB_ALTER_RETIRE -+#define QM_MCR_VERB_ALTER_OOS QM_MCC_VERB_ALTER_OOS -+#define QM_MCR_RESULT_NULL 0x00 -+#define QM_MCR_RESULT_OK 0xf0 -+#define QM_MCR_RESULT_ERR_FQID 0xf1 -+#define QM_MCR_RESULT_ERR_FQSTATE 0xf2 -+#define QM_MCR_RESULT_ERR_NOTEMPTY 0xf3 /* OOS fails if FQ is !empty */ -+#define QM_MCR_RESULT_ERR_BADCHANNEL 0xf4 -+#define QM_MCR_RESULT_PENDING 0xf8 -+#define QM_MCR_RESULT_ERR_BADCOMMAND 0xff -+#define QM_MCR_NP_STATE_FE 0x10 -+#define QM_MCR_NP_STATE_R 0x08 -+#define QM_MCR_NP_STATE_MASK 0x07 /* Reads FQD::STATE; */ -+#define QM_MCR_NP_STATE_OOS 0x00 -+#define QM_MCR_NP_STATE_RETIRED 0x01 -+#define QM_MCR_NP_STATE_TEN_SCHED 0x02 -+#define QM_MCR_NP_STATE_TRU_SCHED 0x03 -+#define QM_MCR_NP_STATE_PARKED 0x04 -+#define QM_MCR_NP_STATE_ACTIVE 0x05 -+#define QM_MCR_NP_PTR_MASK 0x07ff /* for RA[12] & OD[123] */ -+#define QM_MCR_NP_RA1_NRA(v) (((v) >> 14) & 0x3) /* FQD::NRA */ -+#define QM_MCR_NP_RA2_IT(v) (((v) >> 14) & 0x1) /* FQD::IT */ -+#define QM_MCR_NP_OD1_NOD(v) (((v) >> 14) & 0x3) /* FQD::NOD */ -+#define QM_MCR_NP_OD3_NPC(v) (((v) >> 14) & 0x3) /* FQD::NPC */ -+#define QM_MCR_FQS_ORLPRESENT 0x02 /* ORL fragments to come */ -+#define QM_MCR_FQS_NOTEMPTY 0x01 /* FQ has enqueued frames */ -+/* This extracts the state for congestion group 'n' from a query response. -+ * Eg. -+ * u8 cgr = [...]; -+ * struct qm_mc_result *res = [...]; -+ * printf("congestion group %d congestion state: %d\n", cgr, -+ * QM_MCR_QUERYCONGESTION(&res->querycongestion.state, cgr)); -+ */ -+#define __CGR_WORD(num) (num >> 5) -+#define __CGR_SHIFT(num) (num & 0x1f) -+#define __CGR_NUM (sizeof(struct __qm_mcr_querycongestion) << 3) -+static inline int QM_MCR_QUERYCONGESTION(struct __qm_mcr_querycongestion *p, -+ u8 cgr) -+{ -+ return p->__state[__CGR_WORD(cgr)] & (0x80000000 >> __CGR_SHIFT(cgr)); -+} -+ -+ -+/*********************/ -+/* Utility interface */ -+/*********************/ -+ -+/* Represents an allocator over a range of FQIDs. NB, accesses are not locked, -+ * spinlock them yourself if needed. */ -+struct qman_fqid_pool; -+ -+/* Create/destroy a FQID pool, num must be a multiple of 32. NB, _destroy() -+ * always succeeds, but returns non-zero if there were "leaked" FQID -+ * allocations. */ -+struct qman_fqid_pool *qman_fqid_pool_create(u32 fqid_start, u32 num); -+int qman_fqid_pool_destroy(struct qman_fqid_pool *pool); -+/* Alloc/free a FQID from the range. _alloc() returns zero for success. */ -+int qman_fqid_pool_alloc(struct qman_fqid_pool *pool, u32 *fqid); -+void qman_fqid_pool_free(struct qman_fqid_pool *pool, u32 fqid); -+u32 qman_fqid_pool_used(struct qman_fqid_pool *pool); -+ -+/*******************************************************************/ -+/* Managed (aka "shared" or "mux/demux") portal, high-level i/face */ -+/*******************************************************************/ -+ -+ /* Portal and Frame Queues */ -+ /* ----------------------- */ -+/* Represents a managed portal */ -+struct qman_portal; -+ -+/* This object type represents Qman frame queue descriptors (FQD), it is -+ * cacheline-aligned, and initialised by qman_create_fq(). The structure is -+ * defined further down. */ -+struct qman_fq; -+ -+/* This object type represents a Qman congestion group, it is defined further -+ * down. */ -+struct qman_cgr; -+ -+struct qman_portal_config { -+ /* If the caller enables DQRR stashing (and thus wishes to operate the -+ * portal from only one cpu), this is the logical CPU that the portal -+ * will stash to. Whether stashing is enabled or not, this setting is -+ * also used for any "core-affine" portals, ie. default portals -+ * associated to the corresponding cpu. -1 implies that there is no core -+ * affinity configured. */ -+ int cpu; -+ /* portal interrupt line */ -+ int irq; -+ /* the unique index of this portal */ -+ u32 index; -+ /* Is this portal shared? (If so, it has coarser locking and demuxes -+ * processing on behalf of other CPUs.) */ -+ int is_shared; -+ /* The portal's dedicated channel id, use this value for initialising -+ * frame queues to target this portal when scheduled. */ -+ u16 channel; -+ /* A mask of which pool channels this portal has dequeue access to -+ * (using QM_SDQCR_CHANNELS_POOL(n) for the bitmask) */ -+ u32 pools; -+}; -+ -+/* This enum, and the callback type that returns it, are used when handling -+ * dequeued frames via DQRR. Note that for "null" callbacks registered with the -+ * portal object (for handling dequeues that do not demux because contextB is -+ * NULL), the return value *MUST* be qman_cb_dqrr_consume. */ -+enum qman_cb_dqrr_result { -+ /* DQRR entry can be consumed */ -+ qman_cb_dqrr_consume, -+ /* Like _consume, but requests parking - FQ must be held-active */ -+ qman_cb_dqrr_park, -+ /* Does not consume, for DCA mode only. This allows out-of-order -+ * consumes by explicit calls to qman_dca() and/or the use of implicit -+ * DCA via EQCR entries. */ -+ qman_cb_dqrr_defer, -+ /* Stop processing without consuming this ring entry. Exits the current -+ * qman_poll_dqrr() or interrupt-handling, as appropriate. If within an -+ * interrupt handler, the callback would typically call -+ * qman_irqsource_remove(QM_PIRQ_DQRI) before returning this value, -+ * otherwise the interrupt will reassert immediately. */ -+ qman_cb_dqrr_stop, -+ /* Like qman_cb_dqrr_stop, but consumes the current entry. */ -+ qman_cb_dqrr_consume_stop -+}; -+typedef enum qman_cb_dqrr_result (*qman_cb_dqrr)(struct qman_portal *qm, -+ struct qman_fq *fq, -+ const struct qm_dqrr_entry *dqrr); -+ -+/* This callback type is used when handling ERNs, FQRNs and FQRLs via MR. They -+ * are always consumed after the callback returns. */ -+typedef void (*qman_cb_mr)(struct qman_portal *qm, struct qman_fq *fq, -+ const struct qm_mr_entry *msg); -+ -+/* This callback type is used when handling DCP ERNs */ -+typedef void (*qman_cb_dc_ern)(struct qman_portal *qm, -+ const struct qm_mr_entry *msg); -+ -+/* s/w-visible states. Ie. tentatively scheduled + truly scheduled + active + -+ * held-active + held-suspended are just "sched". Things like "retired" will not -+ * be assumed until it is complete (ie. QMAN_FQ_STATE_CHANGING is set until -+ * then, to indicate it's completing and to gate attempts to retry the retire -+ * command). Note, park commands do not set QMAN_FQ_STATE_CHANGING because it's -+ * technically impossible in the case of enqueue DCAs (which refer to DQRR ring -+ * index rather than the FQ that ring entry corresponds to), so repeated park -+ * commands are allowed (if you're silly enough to try) but won't change FQ -+ * state, and the resulting park notifications move FQs from "sched" to -+ * "parked". */ -+enum qman_fq_state { -+ qman_fq_state_oos, -+ qman_fq_state_parked, -+ qman_fq_state_sched, -+ qman_fq_state_retired -+}; -+ -+/* Frame queue objects (struct qman_fq) are stored within memory passed to -+ * qman_create_fq(), as this allows stashing of caller-provided demux callback -+ * pointers at no extra cost to stashing of (driver-internal) FQ state. If the -+ * caller wishes to add per-FQ state and have it benefit from dequeue-stashing, -+ * they should; -+ * -+ * (a) extend the qman_fq structure with their state; eg. -+ * -+ * // myfq is allocated and driver_fq callbacks filled in; -+ * struct my_fq { -+ * struct qman_fq base; -+ * int an_extra_field; -+ * [ ... add other fields to be associated with each FQ ...] -+ * } *myfq = some_my_fq_allocator(); -+ * struct qman_fq *fq = qman_create_fq(fqid, flags, &myfq->base); -+ * -+ * // in a dequeue callback, access extra fields from 'fq' via a cast; -+ * struct my_fq *myfq = (struct my_fq *)fq; -+ * do_something_with(myfq->an_extra_field); -+ * [...] -+ * -+ * (b) when and if configuring the FQ for context stashing, specify how ever -+ * many cachelines are required to stash 'struct my_fq', to accelerate not -+ * only the Qman driver but the callback as well. -+ */ -+ -+struct qman_fq_cb { -+ qman_cb_dqrr dqrr; /* for dequeued frames */ -+ qman_cb_mr ern; /* for s/w ERNs */ -+ qman_cb_mr fqs; /* frame-queue state changes*/ -+}; -+ -+struct qman_fq { -+ /* Caller of qman_create_fq() provides these demux callbacks */ -+ struct qman_fq_cb cb; -+ /* These are internal to the driver, don't touch. In particular, they -+ * may change, be removed, or extended (so you shouldn't rely on -+ * sizeof(qman_fq) being a constant). */ -+ spinlock_t fqlock; -+ u32 fqid; -+ volatile unsigned long flags; -+ enum qman_fq_state state; -+ int cgr_groupid; -+ struct rb_node node; -+#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP -+ u32 key; -+#endif -+}; -+ -+/* This callback type is used when handling congestion group entry/exit. -+ * 'congested' is non-zero on congestion-entry, and zero on congestion-exit. */ -+typedef void (*qman_cb_cgr)(struct qman_portal *qm, -+ struct qman_cgr *cgr, int congested); -+ -+struct qman_cgr { -+ /* Set these prior to qman_create_cgr() */ -+ u32 cgrid; /* 0..255, but u32 to allow specials like -1, 256, etc.*/ -+ qman_cb_cgr cb; -+ /* These are private to the driver */ -+ u16 chan; /* portal channel this object is created on */ -+ struct list_head node; -+}; -+ -+/* Flags to qman_create_fq() */ -+#define QMAN_FQ_FLAG_NO_ENQUEUE 0x00000001 /* can't enqueue */ -+#define QMAN_FQ_FLAG_NO_MODIFY 0x00000002 /* can only enqueue */ -+#define QMAN_FQ_FLAG_TO_DCPORTAL 0x00000004 /* consumed by CAAM/PME/Fman */ -+#define QMAN_FQ_FLAG_LOCKED 0x00000008 /* multi-core locking */ -+#define QMAN_FQ_FLAG_AS_IS 0x00000010 /* query h/w state */ -+#define QMAN_FQ_FLAG_DYNAMIC_FQID 0x00000020 /* (de)allocate fqid */ -+ -+/* Flags to qman_destroy_fq() */ -+#define QMAN_FQ_DESTROY_PARKED 0x00000001 /* FQ can be parked or OOS */ -+ -+/* Flags from qman_fq_state() */ -+#define QMAN_FQ_STATE_CHANGING 0x80000000 /* 'state' is changing */ -+#define QMAN_FQ_STATE_NE 0x40000000 /* retired FQ isn't empty */ -+#define QMAN_FQ_STATE_ORL 0x20000000 /* retired FQ has ORL */ -+#define QMAN_FQ_STATE_BLOCKOOS 0xe0000000 /* if any are set, no OOS */ -+#define QMAN_FQ_STATE_CGR_EN 0x10000000 /* CGR enabled */ -+#define QMAN_FQ_STATE_VDQCR 0x08000000 /* being volatile dequeued */ -+ -+/* Flags to qman_init_fq() */ -+#define QMAN_INITFQ_FLAG_SCHED 0x00000001 /* schedule rather than park */ -+#define QMAN_INITFQ_FLAG_LOCAL 0x00000004 /* set dest portal */ -+ -+/* Flags to qman_volatile_dequeue() */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+#define QMAN_VOLATILE_FLAG_WAIT 0x00000001 /* wait if VDQCR is in use */ -+#define QMAN_VOLATILE_FLAG_WAIT_INT 0x00000002 /* if wait, interruptible? */ -+#define QMAN_VOLATILE_FLAG_FINISH 0x00000004 /* wait till VDQCR completes */ -+#endif -+ -+/* Flags to qman_enqueue(). NB, the strange numbering is to align with hardware, -+ * bit-wise. (NB: the PME API is sensitive to these precise numberings too, so -+ * any change here should be audited in PME.) */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT -+#define QMAN_ENQUEUE_FLAG_WAIT 0x00010000 /* wait if EQCR is full */ -+#define QMAN_ENQUEUE_FLAG_WAIT_INT 0x00020000 /* if wait, interruptible? */ -+#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC -+#define QMAN_ENQUEUE_FLAG_WAIT_SYNC 0x00000004 /* if wait, until consumed? */ -+#endif -+#endif -+#define QMAN_ENQUEUE_FLAG_WATCH_CGR 0x00080000 /* watch congestion state */ -+#define QMAN_ENQUEUE_FLAG_DCA 0x00008000 /* perform enqueue-DCA */ -+#define QMAN_ENQUEUE_FLAG_DCA_PARK 0x00004000 /* If DCA, requests park */ -+#define QMAN_ENQUEUE_FLAG_DCA_PTR(p) /* If DCA, p is DQRR entry */ \ -+ (((u32)(p) << 2) & 0x00000f00) -+#define QMAN_ENQUEUE_FLAG_C_GREEN 0x00000000 /* choose one C_*** flag */ -+#define QMAN_ENQUEUE_FLAG_C_YELLOW 0x00000008 -+#define QMAN_ENQUEUE_FLAG_C_RED 0x00000010 -+#define QMAN_ENQUEUE_FLAG_C_OVERRIDE 0x00000018 -+/* For the ORP-specific qman_enqueue_orp() variant; -+ * - this flag indicates "Not Last In Sequence", ie. all but the final fragment -+ * of a frame. */ -+#define QMAN_ENQUEUE_FLAG_NLIS 0x01000000 -+/* - this flag performs no enqueue but fills in an ORP sequence number that -+ * would otherwise block it (eg. if a frame has been dropped). */ -+#define QMAN_ENQUEUE_FLAG_HOLE 0x02000000 -+/* - this flag performs no enqueue but advances NESN to the given sequence -+ * number. */ -+#define QMAN_ENQUEUE_FLAG_NESN 0x04000000 -+ -+/* Flags to qman_modify_cgr() */ -+#define QMAN_CGR_FLAG_USE_INIT 0x00000001 -+#define QMAN_CGR_MODE_FRAME 0x00000001 -+ -+ /* Portal Management */ -+ /* ----------------- */ -+/** -+ * qman_get_portal_config - get portal configuration settings -+ * -+ * This returns a read-only view of the current cpu's affine portal settings. -+ */ -+const struct qman_portal_config *qman_get_portal_config(void); -+ -+/** -+ * qman_irqsource_get - return the portal work that is interrupt-driven -+ * -+ * Returns a bitmask of QM_PIRQ_**I processing sources that are currently -+ * enabled for interrupt handling on the current cpu's affine portal. These -+ * sources will trigger the portal interrupt and the interrupt handler (or a -+ * tasklet/bottom-half it defers to) will perform the corresponding processing -+ * work. The qman_poll_***() functions will only process sources that are not in -+ * this bitmask. If the current CPU is sharing a portal hosted on another CPU, -+ * this always returns zero. -+ */ -+u32 qman_irqsource_get(void); -+ -+/** -+ * qman_irqsource_add - add processing sources to be interrupt-driven -+ * @bits: bitmask of QM_PIRQ_**I processing sources -+ * -+ * Adds processing sources that should be interrupt-driven (rather than -+ * processed via qman_poll_***() functions). Returns zero for success, or -+ * -EINVAL if the current CPU is sharing a portal hosted on another CPU. -+ */ -+int qman_irqsource_add(u32 bits); -+ -+/** -+ * qman_irqsource_remove - remove processing sources from being interrupt-driven -+ * @bits: bitmask of QM_PIRQ_**I processing sources -+ * -+ * Removes processing sources from being interrupt-driven, so that they will -+ * instead be processed via qman_poll_***() functions. Returns zero for success, -+ * or -EINVAL if the current CPU is sharing a portal hosted on another CPU. -+ */ -+int qman_irqsource_remove(u32 bits); -+ -+/** -+ * qman_affine_cpus - return a mask of cpus that have affine portals -+ */ -+const cpumask_t *qman_affine_cpus(void); -+ -+/** -+ * qman_affine_channel - return the channel ID of an portal -+ * @cpu: the cpu whose affine portal is the subject of the query -+ * -+ * If @cpu is -1, the affine portal for the current CPU will be used. It is a -+ * bug to call this function for any value of @cpu (other than -1) that is not a -+ * member of the mask returned from qman_affine_cpus(). -+ */ -+u16 qman_affine_channel(int cpu); -+ -+/** -+ * qman_get_affine_portal - return the portal pointer affine to cpu -+ * @cpu: the cpu whose affine portal is the subject of the query -+ * -+ */ -+void *qman_get_affine_portal(int cpu); -+ -+/** -+ * qman_poll_dqrr - process DQRR (fast-path) entries -+ * @limit: the maximum number of DQRR entries to process -+ * -+ * Use of this function requires that DQRR processing not be interrupt-driven. -+ * Ie. the value returned by qman_irqsource_get() should not include -+ * QM_PIRQ_DQRI. If the current CPU is sharing a portal hosted on another CPU, -+ * this function will return -EINVAL, otherwise the return value is >=0 and -+ * represents the number of DQRR entries processed. -+ */ -+int qman_poll_dqrr(unsigned int limit); -+ -+/** -+ * qman_poll_slow - process anything (except DQRR) that isn't interrupt-driven. -+ * -+ * This function does any portal processing that isn't interrupt-driven. If the -+ * current CPU is sharing a portal hosted on another CPU, this function will -+ * return (u32)-1, otherwise the return value is a bitmask of QM_PIRQ_* sources -+ * indicating what interrupt sources were actually processed by the call. -+ */ -+u32 qman_poll_slow(void); -+ -+/** -+ * qman_poll - legacy wrapper for qman_poll_dqrr() and qman_poll_slow() -+ * -+ * Dispatcher logic on a cpu can use this to trigger any maintenance of the -+ * affine portal. There are two classes of portal processing in question; -+ * fast-path (which involves demuxing dequeue ring (DQRR) entries and tracking -+ * enqueue ring (EQCR) consumption), and slow-path (which involves EQCR -+ * thresholds, congestion state changes, etc). This function does whatever -+ * processing is not triggered by interrupts. -+ * -+ * Note, if DQRR and some slow-path processing are poll-driven (rather than -+ * interrupt-driven) then this function uses a heuristic to determine how often -+ * to run slow-path processing - as slow-path processing introduces at least a -+ * minimum latency each time it is run, whereas fast-path (DQRR) processing is -+ * close to zero-cost if there is no work to be done. Applications can tune this -+ * behaviour themselves by using qman_poll_dqrr() and qman_poll_slow() directly -+ * rather than going via this wrapper. -+ */ -+void qman_poll(void); -+ -+/** -+ * qman_stop_dequeues - Stop h/w dequeuing to the s/w portal -+ * -+ * Disables DQRR processing of the portal. This is reference-counted, so -+ * qman_start_dequeues() must be called as many times as qman_stop_dequeues() to -+ * truly re-enable dequeuing. -+ */ -+void qman_stop_dequeues(void); -+ -+/** -+ * qman_start_dequeues - (Re)start h/w dequeuing to the s/w portal -+ * -+ * Enables DQRR processing of the portal. This is reference-counted, so -+ * qman_start_dequeues() must be called as many times as qman_stop_dequeues() to -+ * truly re-enable dequeuing. -+ */ -+void qman_start_dequeues(void); -+ -+/** -+ * qman_static_dequeue_add - Add pool channels to the portal SDQCR -+ * @pools: bit-mask of pool channels, using QM_SDQCR_CHANNELS_POOL(n) -+ * -+ * Adds a set of pool channels to the portal's static dequeue command register -+ * (SDQCR). The requested pools are limited to those the portal has dequeue -+ * access to. -+ */ -+void qman_static_dequeue_add(u32 pools); -+ -+/** -+ * qman_static_dequeue_del - Remove pool channels from the portal SDQCR -+ * @pools: bit-mask of pool channels, using QM_SDQCR_CHANNELS_POOL(n) -+ * -+ * Removes a set of pool channels from the portal's static dequeue command -+ * register (SDQCR). The requested pools are limited to those the portal has -+ * dequeue access to. -+ */ -+void qman_static_dequeue_del(u32 pools); -+ -+/** -+ * qman_static_dequeue_get - return the portal's current SDQCR -+ * -+ * Returns the portal's current static dequeue command register (SDQCR). The -+ * entire register is returned, so if only the currently-enabled pool channels -+ * are desired, mask the return value with QM_SDQCR_CHANNELS_POOL_MASK. -+ */ -+u32 qman_static_dequeue_get(void); -+ -+/** -+ * qman_dca - Perform a Discrete Consumption Acknowledgement -+ * @dq: the DQRR entry to be consumed -+ * @park_request: indicates whether the held-active @fq should be parked -+ * -+ * Only allowed in DCA-mode portals, for DQRR entries whose handler callback had -+ * previously returned 'qman_cb_dqrr_defer'. NB, as with the other APIs, this -+ * does not take a 'portal' argument but implies the core affine portal from the -+ * cpu that is currently executing the function. For reasons of locking, this -+ * function must be called from the same CPU as that which processed the DQRR -+ * entry in the first place. -+ */ -+void qman_dca(struct qm_dqrr_entry *dq, int park_request); -+ -+/** -+ * qman_eqcr_is_empty - Determine if portal's EQCR is empty -+ * -+ * For use in situations where a cpu-affine caller needs to determine when all -+ * enqueues for the local portal have been processed by Qman but can't use the -+ * QMAN_ENQUEUE_FLAG_WAIT_SYNC flag to do this from the final qman_enqueue(). -+ * The function forces tracking of EQCR consumption (which normally doesn't -+ * happen until enqueue processing needs to find space to put new enqueue -+ * commands), and returns zero if the ring still has unprocessed entries, -+ * non-zero if it is empty. -+ */ -+int qman_eqcr_is_empty(void); -+ -+/** -+ * qman_set_dc_ern - Set the handler for DCP enqueue rejection notifications -+ * @handler: callback for processing DCP ERNs -+ * @affine: whether this handler is specific to the locally affine portal -+ * -+ * If a hardware block's interface to Qman (ie. its direct-connect portal, or -+ * DCP) is configured not to receive enqueue rejections, then any enqueues -+ * through that DCP that are rejected will be sent to a given software portal. -+ * If @affine is non-zero, then this handler will only be used for DCP ERNs -+ * received on the portal affine to the current CPU. If multiple CPUs share a -+ * portal and they all call this function, they will be setting the handler for -+ * the same portal! If @affine is zero, then this handler will be global to all -+ * portals handled by this instance of the driver. Only those portals that do -+ * not have their own affine handler will use the global handler. -+ */ -+void qman_set_dc_ern(qman_cb_dc_ern handler, int affine); -+ -+ /* FQ management */ -+ /* ------------- */ -+/** -+ * qman_create_fq - Allocates a FQ -+ * @fqid: the index of the FQD to encapsulate, must be "Out of Service" -+ * @flags: bit-mask of QMAN_FQ_FLAG_*** options -+ * @fq: memory for storing the 'fq', with callbacks filled in -+ * -+ * Creates a frame queue object for the given @fqid, unless the -+ * QMAN_FQ_FLAG_DYNAMIC_FQID flag is set in @flags, in which case a FQID is -+ * dynamically allocated (or the function fails if none are available). Once -+ * created, the caller should not touch the memory at 'fq' except as extended to -+ * adjacent memory for user-defined fields (see the definition of "struct -+ * qman_fq" for more info). NO_MODIFY is only intended for enqueuing to -+ * pre-existing frame-queues that aren't to be otherwise interfered with, it -+ * prevents all other modifications to the frame queue. The TO_DCPORTAL flag -+ * causes the driver to honour any contextB modifications requested in the -+ * qm_init_fq() API, as this indicates the frame queue will be consumed by a -+ * direct-connect portal (PME, CAAM, or Fman). When frame queues are consumed by -+ * software portals, the contextB field is controlled by the driver and can't be -+ * modified by the caller. If the AS_IS flag is specified, management commands -+ * will be used on portal @p to query state for frame queue @fqid and construct -+ * a frame queue object based on that, rather than assuming/requiring that it be -+ * Out of Service. -+ */ -+int qman_create_fq(u32 fqid, u32 flags, struct qman_fq *fq); -+ -+/** -+ * qman_destroy_fq - Deallocates a FQ -+ * @fq: the frame queue object to release -+ * @flags: bit-mask of QMAN_FQ_FREE_*** options -+ * -+ * The memory for this frame queue object ('fq' provided in qman_create_fq()) is -+ * not deallocated but the caller regains ownership, to do with as desired. The -+ * FQ must be in the 'out-of-service' state unless the QMAN_FQ_FREE_PARKED flag -+ * is specified, in which case it may also be in the 'parked' state. -+ */ -+void qman_destroy_fq(struct qman_fq *fq, u32 flags); -+ -+/** -+ * qman_fq_fqid - Queries the frame queue ID of a FQ object -+ * @fq: the frame queue object to query -+ */ -+u32 qman_fq_fqid(struct qman_fq *fq); -+ -+/** -+ * qman_fq_state - Queries the state of a FQ object -+ * @fq: the frame queue object to query -+ * @state: pointer to state enum to return the FQ scheduling state -+ * @flags: pointer to state flags to receive QMAN_FQ_STATE_*** bitmask -+ * -+ * Queries the state of the FQ object, without performing any h/w commands. -+ * This captures the state, as seen by the driver, at the time the function -+ * executes. -+ */ -+void qman_fq_state(struct qman_fq *fq, enum qman_fq_state *state, u32 *flags); -+ -+/** -+ * qman_init_fq - Initialises FQ fields, leaves the FQ "parked" or "scheduled" -+ * @fq: the frame queue object to modify, must be 'parked' or new. -+ * @flags: bit-mask of QMAN_INITFQ_FLAG_*** options -+ * @opts: the FQ-modification settings, as defined in the low-level API -+ * -+ * The @opts parameter comes from the low-level portal API. Select -+ * QMAN_INITFQ_FLAG_SCHED in @flags to cause the frame queue to be scheduled -+ * rather than parked. NB, @opts can be NULL. -+ * -+ * Note that some fields and options within @opts may be ignored or overwritten -+ * by the driver; -+ * 1. the 'count' and 'fqid' fields are always ignored (this operation only -+ * affects one frame queue: @fq). -+ * 2. the QM_INITFQ_WE_CONTEXTB option of the 'we_mask' field and the associated -+ * 'fqd' structure's 'context_b' field are sometimes overwritten; -+ * - if @fq was not created with QMAN_FQ_FLAG_TO_DCPORTAL, then context_b is -+ * initialised to a value used by the driver for demux. -+ * - if context_b is initialised for demux, so is context_a in case stashing -+ * is requested (see item 4). -+ * (So caller control of context_b is only possible for TO_DCPORTAL frame queue -+ * objects.) -+ * 3. if @flags contains QMAN_INITFQ_FLAG_LOCAL, the 'fqd' structure's -+ * 'dest::channel' field will be overwritten to match the portal used to issue -+ * the command. If the WE_DESTWQ write-enable bit had already been set by the -+ * caller, the channel workqueue will be left as-is, otherwise the write-enable -+ * bit is set and the workqueue is set to a default of 4. If the "LOCAL" flag -+ * isn't set, the destination channel/workqueue fields and the write-enable bit -+ * are left as-is. -+ * 4. if the driver overwrites context_a/b for demux, then if -+ * QM_INITFQ_WE_CONTEXTA is set, the driver will only overwrite -+ * context_a.address fields and will leave the stashing fields provided by the -+ * user alone, otherwise it will zero out the context_a.stashing fields. -+ */ -+int qman_init_fq(struct qman_fq *fq, u32 flags, struct qm_mcc_initfq *opts); -+ -+/** -+ * qman_schedule_fq - Schedules a FQ -+ * @fq: the frame queue object to schedule, must be 'parked' -+ * -+ * Schedules the frame queue, which must be Parked, which takes it to -+ * Tentatively-Scheduled or Truly-Scheduled depending on its fill-level. -+ */ -+int qman_schedule_fq(struct qman_fq *fq); -+ -+/** -+ * qman_retire_fq - Retires a FQ -+ * @fq: the frame queue object to retire -+ * @flags: FQ flags (as per qman_fq_state) if retirement completes immediately -+ * -+ * Retires the frame queue. This returns zero if it succeeds immediately, +1 if -+ * the retirement was started asynchronously, otherwise it returns negative for -+ * failure. When this function returns zero, @flags is set to indicate whether -+ * the retired FQ is empty and/or whether it has any ORL fragments (to show up -+ * as ERNs). Otherwise the corresponding flags will be known when a subsequent -+ * FQRN message shows up on the portal's message ring. -+ * -+ * NB, if the retirement is asynchronous (the FQ was in the Truly Scheduled or -+ * Active state), the completion will be via the message ring as a FQRN - but -+ * the corresponding callback may occur before this function returns!! Ie. the -+ * caller should be prepared to accept the callback as the function is called, -+ * not only once it has returned. -+ */ -+int qman_retire_fq(struct qman_fq *fq, u32 *flags); -+ -+/** -+ * qman_oos_fq - Puts a FQ "out of service" -+ * @fq: the frame queue object to be put out-of-service, must be 'retired' -+ * -+ * The frame queue must be retired and empty, and if any order restoration list -+ * was released as ERNs at the time of retirement, they must all be consumed. -+ */ -+int qman_oos_fq(struct qman_fq *fq); -+ -+/** -+ * qman_fq_flow_control - Set the XON/XOFF state of a FQ -+ * @fq: the frame queue object to be set to XON/XOFF state, must not be 'oos', -+ * or 'retired' or 'parked' state -+ * @xon: boolean to set fq in XON or XOFF state -+ * -+ * The frame should be in Tentatively Scheduled state or Truly Schedule sate, -+ * otherwise the IFSI interrupt will be asserted. -+ */ -+int qman_fq_flow_control(struct qman_fq *fq, int xon); -+ -+/** -+ * qman_query_fq - Queries FQD fields (via h/w query command) -+ * @fq: the frame queue object to be queried -+ * @fqd: storage for the queried FQD fields -+ */ -+int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd); -+ -+/** -+ * qman_query_fq_np - Queries non-programmable FQD fields -+ * @fq: the frame queue object to be queried -+ * @np: storage for the queried FQD fields -+ */ -+int qman_query_fq_np(struct qman_fq *fq, struct qm_mcr_queryfq_np *np); -+ -+/** -+ * qman_query_wq - Queries work queue lengths -+ * @query_dedicated: If non-zero, query length of WQs in the channel dedicated -+ * to this software portal. Otherwise, query length of WQs in a -+ * channel specified in wq. -+ * @wq: storage for the queried WQs lengths. Also specified the channel to -+ * to query if query_dedicated is zero. -+ */ -+int qman_query_wq(u8 query_dedicated, struct qm_mcr_querywq *wq); -+ -+/** -+ * qman_volatile_dequeue - Issue a volatile dequeue command -+ * @fq: the frame queue object to dequeue from -+ * @flags: a bit-mask of QMAN_VOLATILE_FLAG_*** options -+ * @vdqcr: bit mask of QM_VDQCR_*** options, as per qm_dqrr_vdqcr_set() -+ * -+ * Attempts to lock access to the portal's VDQCR volatile dequeue functionality. -+ * The function will block and sleep if QMAN_VOLATILE_FLAG_WAIT is specified and -+ * the VDQCR is already in use, otherwise returns non-zero for failure. If -+ * QMAN_VOLATILE_FLAG_FINISH is specified, the function will only return once -+ * the VDQCR command has finished executing (ie. once the callback for the last -+ * DQRR entry resulting from the VDQCR command has been called). If not using -+ * the FINISH flag, completion can be determined either by detecting the -+ * presence of the QM_DQRR_STAT_UNSCHEDULED and QM_DQRR_STAT_DQCR_EXPIRED bits -+ * in the "stat" field of the "struct qm_dqrr_entry" passed to the FQ's dequeue -+ * callback, or by waiting for the QMAN_FQ_STATE_VDQCR bit to disappear from the -+ * "flags" retrieved from qman_fq_state(). -+ */ -+int qman_volatile_dequeue(struct qman_fq *fq, u32 flags, u32 vdqcr); -+ -+/** -+ * qman_enqueue - Enqueue a frame to a frame queue -+ * @fq: the frame queue object to enqueue to -+ * @fd: a descriptor of the frame to be enqueued -+ * @flags: bit-mask of QMAN_ENQUEUE_FLAG_*** options -+ * -+ * Fills an entry in the EQCR of portal @qm to enqueue the frame described by -+ * @fd. The descriptor details are copied from @fd to the EQCR entry, the 'pid' -+ * field is ignored. The return value is non-zero on error, such as ring full -+ * (and FLAG_WAIT not specified), congestion avoidance (FLAG_WATCH_CGR -+ * specified), etc. If the ring is full and FLAG_WAIT is specified, this -+ * function will block. If FLAG_INTERRUPT is set, the EQCI bit of the portal -+ * interrupt will assert when Qman consumes the EQCR entry (subject to "status -+ * disable", "enable", and "inhibit" registers). If FLAG_DCA is set, Qman will -+ * perform an implied "discrete consumption acknowledgement" on the dequeue -+ * ring's (DQRR) entry, at the ring index specified by the FLAG_DCA_IDX(x) -+ * macro. (As an alternative to issuing explicit DCA actions on DQRR entries, -+ * this implicit DCA can delay the release of a "held active" frame queue -+ * corresponding to a DQRR entry until Qman consumes the EQCR entry - providing -+ * order-preservation semantics in packet-forwarding scenarios.) If FLAG_DCA is -+ * set, then FLAG_DCA_PARK can also be set to imply that the DQRR consumption -+ * acknowledgement should "park request" the "held active" frame queue. Ie. -+ * when the portal eventually releases that frame queue, it will be left in the -+ * Parked state rather than Tentatively Scheduled or Truly Scheduled. If the -+ * portal is watching congestion groups, the QMAN_ENQUEUE_FLAG_WATCH_CGR flag -+ * is requested, and the FQ is a member of a congestion group, then this -+ * function returns -EAGAIN if the congestion group is currently congested. -+ * Note, this does not eliminate ERNs, as the async interface means we can be -+ * sending enqueue commands to an un-congested FQ that becomes congested before -+ * the enqueue commands are processed, but it does minimise needless thrashing -+ * of an already busy hardware resource by throttling many of the to-be-dropped -+ * enqueues "at the source". -+ */ -+int qman_enqueue(struct qman_fq *fq, const struct qm_fd *fd, u32 flags); -+ -+typedef int (*qman_cb_precommit) (void *arg); -+/** -+ * qman_enqueue_precommit - Enqueue a frame to a frame queue and call cb -+ * @fq: the frame queue object to enqueue to -+ * @fd: a descriptor of the frame to be enqueued -+ * @flags: bit-mask of QMAN_ENQUEUE_FLAG_*** options -+ * @cb: user supplied callback function to invoke before writing commit verb. -+ * @cb_arg: callback function argument -+ * -+ * This is similar to qman_enqueue except that it will invoke a user supplied -+ * callback function just before writng the commit verb. This is useful -+ * when the user want to do something *just before* enqueuing the request and -+ * the enqueue can't fail. -+ */ -+int qman_enqueue_precommit(struct qman_fq *fq, const struct qm_fd *fd, -+ u32 flags, qman_cb_precommit cb, void *cb_arg); -+ -+/** -+ * qman_enqueue_orp - Enqueue a frame to a frame queue using an ORP -+ * @fq: the frame queue object to enqueue to -+ * @fd: a descriptor of the frame to be enqueued -+ * @flags: bit-mask of QMAN_ENQUEUE_FLAG_*** options -+ * @orp: the frame queue object used as an order restoration point. -+ * @orp_seqnum: the sequence number of this frame in the order restoration path -+ * -+ * Similar to qman_enqueue(), but with the addition of an Order Restoration -+ * Point (@orp) and corresponding sequence number (@orp_seqnum) for this -+ * enqueue operation to employ order restoration. Each frame queue object acts -+ * as an Order Definition Point (ODP) by providing each frame dequeued from it -+ * with an incrementing sequence number, this value is generally ignored unless -+ * that sequence of dequeued frames will need order restoration later. Each -+ * frame queue object also encapsulates an Order Restoration Point (ORP), which -+ * is a re-assembly context for re-ordering frames relative to their sequence -+ * numbers as they are enqueued. The ORP does not have to be within the frame -+ * queue that receives the enqueued frame, in fact it is usually the frame -+ * queue from which the frames were originally dequeued. For the purposes of -+ * order restoration, multiple frames (or "fragments") can be enqueued for a -+ * single sequence number by setting the QMAN_ENQUEUE_FLAG_NLIS flag for all -+ * enqueues except the final fragment of a given sequence number. Ordering -+ * between sequence numbers is guaranteed, even if fragments of different -+ * sequence numbers are interlaced with one another. Fragments of the same -+ * sequence number will retain the order in which they are enqueued. If no -+ * enqueue is to performed, QMAN_ENQUEUE_FLAG_HOLE indicates that the given -+ * sequence number is to be "skipped" by the ORP logic (eg. if a frame has been -+ * dropped from a sequence), or QMAN_ENQUEUE_FLAG_NESN indicates that the given -+ * sequence number should become the ORP's "Next Expected Sequence Number". -+ * -+ * Side note: a frame queue object can be used purely as an ORP, without -+ * carrying any frames at all. Care should be taken not to deallocate a frame -+ * queue object that is being actively used as an ORP, as a future allocation -+ * of the frame queue object may start using the internal ORP before the -+ * previous use has finished. -+ */ -+int qman_enqueue_orp(struct qman_fq *fq, const struct qm_fd *fd, u32 flags, -+ struct qman_fq *orp, u16 orp_seqnum); -+ -+/** -+ * qman_alloc_fqid_range - Allocate a contiguous range of FQIDs -+ * @result: is set by the API to the base FQID of the allocated range -+ * @count: the number of FQIDs required -+ * @align: required alignment of the allocated range -+ * @partial: non-zero if the API can return fewer than @count FQIDs -+ * -+ * Returns the number of frame queues allocated, or a negative error code. If -+ * @partial is non zero, the allocation request may return a smaller range of -+ * FQs than requested (though alignment will be as requested). If @partial is -+ * zero, the return value will either be 'count' or negative. -+ */ -+int qman_alloc_fqid_range(u32 *result, u32 count, u32 align, int partial); -+static inline int qman_alloc_fqid(u32 *result) -+{ -+ int ret = qman_alloc_fqid_range(result, 1, 0, 0); -+ return (ret > 0) ? 0 : ret; -+} -+ -+/** -+ * qman_release_fqid_range - Release the specified range of frame queue IDs -+ * @fqid: the base FQID of the range to deallocate -+ * @count: the number of FQIDs in the range -+ * -+ * This function can also be used to seed the allocator with ranges of FQIDs -+ * that it can subsequently allocate from. -+ */ -+void qman_release_fqid_range(u32 fqid, unsigned int count); -+static inline void qman_release_fqid(u32 fqid) -+{ -+ qman_release_fqid_range(fqid, 1); -+} -+ -+void qman_seed_fqid_range(u32 fqid, unsigned int count); -+ -+ -+int qman_shutdown_fq(u32 fqid); -+ -+/** -+ * qman_reserve_fqid_range - Reserve the specified range of frame queue IDs -+ * @fqid: the base FQID of the range to deallocate -+ * @count: the number of FQIDs in the range -+ */ -+int qman_reserve_fqid_range(u32 fqid, unsigned int count); -+static inline int qman_reserve_fqid(u32 fqid) -+{ -+ return qman_reserve_fqid_range(fqid, 1); -+} -+ -+ /* Pool-channel management */ -+ /* ----------------------- */ -+/** -+ * qman_alloc_pool_range - Allocate a contiguous range of pool-channel IDs -+ * @result: is set by the API to the base pool-channel ID of the allocated range -+ * @count: the number of pool-channel IDs required -+ * @align: required alignment of the allocated range -+ * @partial: non-zero if the API can return fewer than @count -+ * -+ * Returns the number of pool-channel IDs allocated, or a negative error code. -+ * If @partial is non zero, the allocation request may return a smaller range of -+ * than requested (though alignment will be as requested). If @partial is zero, -+ * the return value will either be 'count' or negative. -+ */ -+int qman_alloc_pool_range(u32 *result, u32 count, u32 align, int partial); -+static inline int qman_alloc_pool(u32 *result) -+{ -+ int ret = qman_alloc_pool_range(result, 1, 0, 0); -+ return (ret > 0) ? 0 : ret; -+} -+ -+/** -+ * qman_release_pool_range - Release the specified range of pool-channel IDs -+ * @id: the base pool-channel ID of the range to deallocate -+ * @count: the number of pool-channel IDs in the range -+ */ -+void qman_release_pool_range(u32 id, unsigned int count); -+static inline void qman_release_pool(u32 id) -+{ -+ qman_release_pool_range(id, 1); -+} -+ -+/** -+ * qman_reserve_pool_range - Reserve the specified range of pool-channel IDs -+ * @id: the base pool-channel ID of the range to reserve -+ * @count: the number of pool-channel IDs in the range -+ */ -+int qman_reserve_pool_range(u32 id, unsigned int count); -+static inline int qman_reserve_pool(u32 id) -+{ -+ return qman_reserve_pool_range(id, 1); -+} -+ -+void qman_seed_pool_range(u32 id, unsigned int count); -+ -+ /* CGR management */ -+ /* -------------- */ -+/** -+ * qman_create_cgr - Register a congestion group object -+ * @cgr: the 'cgr' object, with fields filled in -+ * @flags: QMAN_CGR_FLAG_* values -+ * @opts: optional state of CGR settings -+ * -+ * Registers this object to receiving congestion entry/exit callbacks on the -+ * portal affine to the cpu portal on which this API is executed. If opts is -+ * NULL then only the callback (cgr->cb) function is registered. If @flags -+ * contains QMAN_CGR_FLAG_USE_INIT, then an init hw command (which will reset -+ * any unspecified parameters) will be used rather than a modify hw hardware -+ * (which only modifies the specified parameters). -+ */ -+int qman_create_cgr(struct qman_cgr *cgr, u32 flags, -+ struct qm_mcc_initcgr *opts); -+ -+/** -+ * qman_create_cgr_to_dcp - Register a congestion group object to DCP portal -+ * @cgr: the 'cgr' object, with fields filled in -+ * @flags: QMAN_CGR_FLAG_* values -+ * @dcp_portal: the DCP portal to which the cgr object is registered. -+ * @opts: optional state of CGR settings -+ * -+ */ -+int qman_create_cgr_to_dcp(struct qman_cgr *cgr, u32 flags, u16 dcp_portal, -+ struct qm_mcc_initcgr *opts); -+ -+/** -+ * qman_delete_cgr - Deregisters a congestion group object -+ * @cgr: the 'cgr' object to deregister -+ * -+ * "Unplugs" this CGR object from the portal affine to the cpu on which this API -+ * is executed. This must be excuted on the same affine portal on which it was -+ * created. -+ */ -+int qman_delete_cgr(struct qman_cgr *cgr); -+ -+/** -+ * qman_delete_cgr_safe - Deregisters a congestion group object from any CPU -+ * @cgr: the 'cgr' object to deregister -+ * -+ * This will select the proper CPU and run there qman_delete_cgr(). -+ */ -+void qman_delete_cgr_safe(struct qman_cgr *cgr); -+ -+/** -+ * qman_modify_cgr - Modify CGR fields -+ * @cgr: the 'cgr' object to modify -+ * @flags: QMAN_CGR_FLAG_* values -+ * @opts: the CGR-modification settings -+ * -+ * The @opts parameter comes from the low-level portal API, and can be NULL. -+ * Note that some fields and options within @opts may be ignored or overwritten -+ * by the driver, in particular the 'cgrid' field is ignored (this operation -+ * only affects the given CGR object). If @flags contains -+ * QMAN_CGR_FLAG_USE_INIT, then an init hw command (which will reset any -+ * unspecified parameters) will be used rather than a modify hw hardware (which -+ * only modifies the specified parameters). -+ */ -+int qman_modify_cgr(struct qman_cgr *cgr, u32 flags, -+ struct qm_mcc_initcgr *opts); -+ -+/** -+* qman_query_cgr - Queries CGR fields -+* @cgr: the 'cgr' object to query -+* @result: storage for the queried congestion group record -+*/ -+int qman_query_cgr(struct qman_cgr *cgr, struct qm_mcr_querycgr *result); -+ -+/** -+ * qman_query_congestion - Queries the state of all congestion groups -+ * @congestion: storage for the queried state of all congestion groups -+ */ -+int qman_query_congestion(struct qm_mcr_querycongestion *congestion); -+ -+/** -+ * qman_alloc_cgrid_range - Allocate a contiguous range of CGR IDs -+ * @result: is set by the API to the base CGR ID of the allocated range -+ * @count: the number of CGR IDs required -+ * @align: required alignment of the allocated range -+ * @partial: non-zero if the API can return fewer than @count -+ * -+ * Returns the number of CGR IDs allocated, or a negative error code. -+ * If @partial is non zero, the allocation request may return a smaller range of -+ * than requested (though alignment will be as requested). If @partial is zero, -+ * the return value will either be 'count' or negative. -+ */ -+int qman_alloc_cgrid_range(u32 *result, u32 count, u32 align, int partial); -+static inline int qman_alloc_cgrid(u32 *result) -+{ -+ int ret = qman_alloc_cgrid_range(result, 1, 0, 0); -+ return (ret > 0) ? 0 : ret; -+} -+ -+/** -+ * qman_release_cgrid_range - Release the specified range of CGR IDs -+ * @id: the base CGR ID of the range to deallocate -+ * @count: the number of CGR IDs in the range -+ */ -+void qman_release_cgrid_range(u32 id, unsigned int count); -+static inline void qman_release_cgrid(u32 id) -+{ -+ qman_release_cgrid_range(id, 1); -+} -+ -+/** -+ * qman_reserve_cgrid_range - Reserve the specified range of CGR ID -+ * @id: the base CGR ID of the range to reserve -+ * @count: the number of CGR IDs in the range -+ */ -+int qman_reserve_cgrid_range(u32 id, unsigned int count); -+static inline int qman_reserve_cgrid(u32 id) -+{ -+ return qman_reserve_cgrid_range(id, 1); -+} -+ -+void qman_seed_cgrid_range(u32 id, unsigned int count); -+ -+ -+ /* Helpers */ -+ /* ------- */ -+/** -+ * qman_poll_fq_for_init - Check if an FQ has been initialised from OOS -+ * @fqid: the FQID that will be initialised by other s/w -+ * -+ * In many situations, a FQID is provided for communication between s/w -+ * entities, and whilst the consumer is responsible for initialising and -+ * scheduling the FQ, the producer(s) generally create a wrapper FQ object using -+ * and only call qman_enqueue() (no FQ initialisation, scheduling, etc). Ie; -+ * qman_create_fq(..., QMAN_FQ_FLAG_NO_MODIFY, ...); -+ * However, data can not be enqueued to the FQ until it is initialised out of -+ * the OOS state - this function polls for that condition. It is particularly -+ * useful for users of IPC functions - each endpoint's Rx FQ is the other -+ * endpoint's Tx FQ, so each side can initialise and schedule their Rx FQ object -+ * and then use this API on the (NO_MODIFY) Tx FQ object in order to -+ * synchronise. The function returns zero for success, +1 if the FQ is still in -+ * the OOS state, or negative if there was an error. -+ */ -+static inline int qman_poll_fq_for_init(struct qman_fq *fq) -+{ -+ struct qm_mcr_queryfq_np np; -+ int err; -+ err = qman_query_fq_np(fq, &np); -+ if (err) -+ return err; -+ if ((np.state & QM_MCR_NP_STATE_MASK) == QM_MCR_NP_STATE_OOS) -+ return 1; -+ return 0; -+} -+ -+ /* -------------- */ -+ /* CEETM :: types */ -+ /* -------------- */ -+/** -+ * Token Rate Structure -+ * Shaping rates are based on a "credit" system and a pre-configured h/w -+ * internal timer. The following type represents a shaper "rate" parameter as a -+ * fractional number of "tokens". Here's how it works. This (fractional) number -+ * of tokens is added to the shaper's "credit" every time the h/w timer elapses -+ * (up to a limit which is set by another shaper parameter). Every time a frame -+ * is enqueued through a shaper, the shaper deducts as many tokens as there are -+ * bytes of data in the enqueued frame. A shaper will not allow itself to -+ * enqueue any frames if its token count is negative. As such; -+ * -+ * The rate at which data is enqueued is limited by the -+ * rate at which tokens are added. -+ * -+ * Therefore if the user knows the period between these h/w timer updates in -+ * seconds, they can calculate the maximum traffic rate of the shaper (in -+ * bytes-per-second) from the token rate. And vice versa, they can calculate -+ * the token rate to use in order to achieve a given traffic rate. -+ */ -+struct qm_ceetm_rate { -+ /* The token rate is; whole + (fraction/8192) */ -+ u32 whole:11; /* 0..2047 */ -+ u32 fraction:13; /* 0..8191 */ -+}; -+ -+struct qm_ceetm_weight_code { -+ /* The weight code is; 5 msbits + 3 lsbits */ -+ u8 y:5; -+ u8 x:3; -+}; -+ -+struct qm_ceetm { -+ unsigned int idx; -+ struct list_head sub_portals; -+ struct list_head lnis; -+ unsigned int sp_range[2]; -+ unsigned int lni_range[2]; -+}; -+ -+struct qm_ceetm_sp { -+ struct list_head node; -+ unsigned int idx; -+ unsigned int dcp_idx; -+ int is_claimed; -+ struct qm_ceetm_lni *lni; -+}; -+ -+/* Logical Network Interface */ -+struct qm_ceetm_lni { -+ struct list_head node; -+ unsigned int idx; -+ unsigned int dcp_idx; -+ int is_claimed; -+ struct qm_ceetm_sp *sp; -+ struct list_head channels; -+ int shaper_enable; -+ int shaper_couple; -+ int oal; -+ struct qm_ceetm_rate cr_token_rate; -+ struct qm_ceetm_rate er_token_rate; -+ u16 cr_token_bucket_limit; -+ u16 er_token_bucket_limit; -+}; -+ -+/* Class Queue Channel */ -+struct qm_ceetm_channel { -+ struct list_head node; -+ unsigned int idx; -+ unsigned int lni_idx; -+ unsigned int dcp_idx; -+ struct list_head class_queues; -+ struct list_head ccgs; -+ u8 shaper_enable; -+ u8 shaper_couple; -+ struct qm_ceetm_rate cr_token_rate; -+ struct qm_ceetm_rate er_token_rate; -+ u16 cr_token_bucket_limit; -+ u16 er_token_bucket_limit; -+}; -+ -+struct qm_ceetm_ccg; -+ -+/* This callback type is used when handling congestion entry/exit. The -+ * 'cb_ctx' value is the opaque value associated with ccg object. -+ * 'congested' is non-zero on congestion-entry, and zero on congestion-exit. -+ */ -+typedef void (*qman_cb_ccgr)(struct qm_ceetm_ccg *ccg, void *cb_ctx, -+ int congested); -+ -+/* Class Congestion Group */ -+struct qm_ceetm_ccg { -+ struct qm_ceetm_channel *parent; -+ struct list_head node; -+ struct list_head cb_node; -+ qman_cb_ccgr cb; -+ void *cb_ctx; -+ unsigned int idx; -+}; -+ -+/* Class Queue */ -+struct qm_ceetm_cq { -+ struct qm_ceetm_channel *parent; -+ struct qm_ceetm_ccg *ccg; -+ struct list_head node; -+ unsigned int idx; -+ int is_claimed; -+ struct list_head bound_lfqids; -+ struct list_head binding_node; -+}; -+ -+/* Logical Frame Queue */ -+struct qm_ceetm_lfq { -+ struct qm_ceetm_channel *parent; -+ struct list_head node; -+ unsigned int idx; -+ unsigned int dctidx; -+ u64 context_a; -+ u32 context_b; -+ qman_cb_mr ern; -+}; -+ -+/** -+ * qman_ceetm_bps2tokenrate - Given a desired rate 'bps' measured in bps -+ * (ie. bits-per-second), compute the 'token_rate' fraction that best -+ * approximates that rate. -+ * @bps: the desired shaper rate in bps. -+ * @token_rate: the output token rate computed with the given kbps. -+ * @rounding: dictates how to round if an exact conversion is not possible; if -+ * it is negative then 'token_rate' will round down to the highest value that -+ * does not exceed the desired rate, if it is positive then 'token_rate' will -+ * round up to the lowest value that is greater than or equal to the desired -+ * rate, and if it is zero then it will round to the nearest approximation, -+ * whether that be up or down. -+ * -+ * Return 0 for success, or -EINVAL if prescaler or qman clock is not available. -+ */ -+int qman_ceetm_bps2tokenrate(u64 bps, -+ struct qm_ceetm_rate *token_rate, -+ int rounding); -+ -+/** -+ * qman_ceetm_tokenrate2bps - Given a 'token_rate', compute the -+ * corresponding number of 'bps'. -+ * @token_rate: the input desired token_rate fraction. -+ * @bps: the output shaper rate in bps computed with the give token rate. -+ * @rounding: has the same semantics as the previous function. -+ * -+ * Return 0 for success, or -EINVAL if prescaler or qman clock is not available. -+ */ -+int qman_ceetm_tokenrate2bps(const struct qm_ceetm_rate *token_rate, -+ u64 *bps, -+ int rounding); -+ -+int qman_alloc_ceetm0_channel_range(u32 *result, u32 count, u32 align, -+ int partial); -+static inline int qman_alloc_ceetm0_channel(u32 *result) -+{ -+ int ret = qman_alloc_ceetm0_channel_range(result, 1, 0, 0); -+ return (ret > 0) ? 0 : ret; -+} -+void qman_release_ceetm0_channel_range(u32 channelid, u32 count); -+static inline void qman_release_ceetm0_channelid(u32 channelid) -+{ -+ qman_release_ceetm0_channel_range(channelid, 1); -+} -+ -+int qman_reserve_ceetm0_channel_range(u32 channelid, u32 count); -+static inline int qman_reserve_ceetm0_channelid(u32 channelid) -+{ -+ return qman_reserve_ceetm0_channel_range(channelid, 1); -+} -+ -+void qman_seed_ceetm0_channel_range(u32 channelid, u32 count); -+ -+ -+int qman_alloc_ceetm1_channel_range(u32 *result, u32 count, u32 align, -+ int partial); -+static inline int qman_alloc_ceetm1_channel(u32 *result) -+{ -+ int ret = qman_alloc_ceetm1_channel_range(result, 1, 0, 0); -+ return (ret > 0) ? 0 : ret; -+} -+void qman_release_ceetm1_channel_range(u32 channelid, u32 count); -+static inline void qman_release_ceetm1_channelid(u32 channelid) -+{ -+ qman_release_ceetm1_channel_range(channelid, 1); -+} -+int qman_reserve_ceetm1_channel_range(u32 channelid, u32 count); -+static inline int qman_reserve_ceetm1_channelid(u32 channelid) -+{ -+ return qman_reserve_ceetm1_channel_range(channelid, 1); -+} -+ -+void qman_seed_ceetm1_channel_range(u32 channelid, u32 count); -+ -+ -+int qman_alloc_ceetm0_lfqid_range(u32 *result, u32 count, u32 align, -+ int partial); -+static inline int qman_alloc_ceetm0_lfqid(u32 *result) -+{ -+ int ret = qman_alloc_ceetm0_lfqid_range(result, 1, 0, 0); -+ return (ret > 0) ? 0 : ret; -+} -+void qman_release_ceetm0_lfqid_range(u32 lfqid, u32 count); -+static inline void qman_release_ceetm0_lfqid(u32 lfqid) -+{ -+ qman_release_ceetm0_lfqid_range(lfqid, 1); -+} -+int qman_reserve_ceetm0_lfqid_range(u32 lfqid, u32 count); -+static inline int qman_reserve_ceetm0_lfqid(u32 lfqid) -+{ -+ return qman_reserve_ceetm0_lfqid_range(lfqid, 1); -+} -+ -+void qman_seed_ceetm0_lfqid_range(u32 lfqid, u32 count); -+ -+ -+int qman_alloc_ceetm1_lfqid_range(u32 *result, u32 count, u32 align, -+ int partial); -+static inline int qman_alloc_ceetm1_lfqid(u32 *result) -+{ -+ int ret = qman_alloc_ceetm1_lfqid_range(result, 1, 0, 0); -+ return (ret > 0) ? 0 : ret; -+} -+void qman_release_ceetm1_lfqid_range(u32 lfqid, u32 count); -+static inline void qman_release_ceetm1_lfqid(u32 lfqid) -+{ -+ qman_release_ceetm1_lfqid_range(lfqid, 1); -+} -+int qman_reserve_ceetm1_lfqid_range(u32 lfqid, u32 count); -+static inline int qman_reserve_ceetm1_lfqid(u32 lfqid) -+{ -+ return qman_reserve_ceetm1_lfqid_range(lfqid, 1); -+} -+ -+void qman_seed_ceetm1_lfqid_range(u32 lfqid, u32 count); -+ -+ -+ /* ----------------------------- */ -+ /* CEETM :: sub-portals */ -+ /* ----------------------------- */ -+ -+/** -+ * qman_ceetm_sp_claim - Claims the given sub-portal, provided it is available -+ * to us and configured for traffic-management. -+ * @sp: the returned sub-portal object, if successful. -+ * @dcp_id: specifies the desired Fman block (and thus the relevant CEETM -+ * instance), -+ * @sp_idx" is the desired sub-portal index from 0 to 15. -+ * -+ * Returns zero for success, or -ENODEV if the sub-portal is in use, or -EINVAL -+ * if the sp_idx is out of range. -+ * -+ * Note that if there are multiple driver domains (eg. a linux kernel versus -+ * user-space drivers in USDPAA, or multiple guests running under a hypervisor) -+ * then a sub-portal may be accessible by more than one instance of a qman -+ * driver and so it may be claimed multiple times. If this is the case, it is -+ * up to the system architect to prevent conflicting configuration actions -+ * coming from the different driver domains. The qman drivers do not have any -+ * behind-the-scenes coordination to prevent this from happening. -+ */ -+int qman_ceetm_sp_claim(struct qm_ceetm_sp **sp, -+ enum qm_dc_portal dcp_idx, -+ unsigned int sp_idx); -+ -+/** -+ * qman_ceetm_sp_release - Releases a previously claimed sub-portal. -+ * @sp: the sub-portal to be released. -+ * -+ * Returns 0 for success, or -EBUSY for failure if the dependencies are not -+ * released. -+ */ -+int qman_ceetm_sp_release(struct qm_ceetm_sp *sp); -+ -+ /* ----------------------------------- */ -+ /* CEETM :: logical network interfaces */ -+ /* ----------------------------------- */ -+ -+/** -+ * qman_ceetm_lni_claim - Claims an unclaimed LNI. -+ * @lni: the returned LNI object, if successful. -+ * @dcp_id: specifies the desired Fman block (and thus the relevant CEETM -+ * instance) -+ * @lni_idx: is the desired LNI index. -+ * -+ * Returns zero for success, or -EINVAL on failure, which will happen if the LNI -+ * is not available or has already been claimed (and not yet successfully -+ * released), or lni_dix is out of range. -+ * -+ * Note that there may be multiple driver domains (or instances) that need to -+ * transmit out the same LNI, so this claim is only guaranteeing exclusivity -+ * within the domain of the driver being called. See qman_ceetm_sp_claim() and -+ * qman_ceetm_sp_get_lni() for more information. -+ */ -+int qman_ceetm_lni_claim(struct qm_ceetm_lni **lni, -+ enum qm_dc_portal dcp_id, -+ unsigned int lni_idx); -+ -+/** -+ * qman_ceetm_lni_releaes - Releases a previously claimed LNI. -+ * @lni: the lni needs to be released. -+ * -+ * This will only succeed if all dependent objects have been released. -+ * Returns zero for success, or -EBUSY if the dependencies are not released. -+ */ -+int qman_ceetm_lni_release(struct qm_ceetm_lni *lni); -+ -+/** -+ * qman_ceetm_sp_set_lni -+ * qman_ceetm_sp_get_lni - Set/get the LNI that the sub-portal is currently -+ * mapped to. -+ * @sp: the given sub-portal. -+ * @lni(in "set"function): the LNI object which the sp will be mappaed to. -+ * @lni_idx(in "get" function): the LNI index which the sp is mapped to. -+ * -+ * Returns zero for success, or -EINVAL for the "set" function when this sp-lni -+ * mapping has been set, or configure mapping command returns error, and -+ * -EINVAL for "get" function when this sp-lni mapping is not set or the query -+ * mapping command returns error. -+ * -+ * This may be useful in situations where multiple driver domains have access -+ * to the same sub-portals in order to all be able to transmit out the same -+ * physical interface (perhaps they're on different IP addresses or VPNs, so -+ * Fman is splitting Rx traffic and here we need to converge Tx traffic). In -+ * that case, a control-plane is likely to use qman_ceetm_lni_claim() followed -+ * by qman_ceetm_sp_set_lni() to configure the sub-portal, and other domains -+ * are likely to use qman_ceetm_sp_get_lni() followed by qman_ceetm_lni_claim() -+ * in order to determine the LNI that the control-plane had assigned. This is -+ * why the "get" returns an index, whereas the "set" takes an (already claimed) -+ * LNI object. -+ */ -+int qman_ceetm_sp_set_lni(struct qm_ceetm_sp *sp, -+ struct qm_ceetm_lni *lni); -+int qman_ceetm_sp_get_lni(struct qm_ceetm_sp *sp, -+ unsigned int *lni_idx); -+ -+/** -+ * qman_ceetm_lni_enable_shaper -+ * qman_ceetm_lni_disable_shaper - Enables/disables shaping on the LNI. -+ * @lni: the given LNI. -+ * @coupled: indicates whether CR and ER are coupled. -+ * @oal: the overhead accounting length which is added to the actual length of -+ * each frame when performing shaper calculations. -+ * -+ * When the number of (unused) committed-rate tokens reach the committed-rate -+ * token limit, 'coupled' indicates whether surplus tokens should be added to -+ * the excess-rate token count (up to the excess-rate token limit). -+ * When LNI is claimed, the shaper is disabled by default. The enable function -+ * will turn on this shaper for this lni. -+ * Whenever a claimed LNI is first enabled for shaping, its committed and -+ * excess token rates and limits are zero, so will need to be changed to do -+ * anything useful. The shaper can subsequently be enabled/disabled without -+ * resetting the shaping parameters, but the shaping parameters will be reset -+ * when the LNI is released. -+ * -+ * Returns zero for success, or errno for "enable" function in the cases as: -+ * a) -EINVAL if the shaper is already enabled, -+ * b) -EIO if the configure shaper command returns error. -+ * For "disable" function, returns: -+ * a) -EINVAL if the shaper is has already disabled. -+ * b) -EIO if calling configure shaper command returns error. -+ */ -+int qman_ceetm_lni_enable_shaper(struct qm_ceetm_lni *lni, int coupled, -+ int oal); -+int qman_ceetm_lni_disable_shaper(struct qm_ceetm_lni *lni); -+ -+/** -+ * qman_ceetm_lni_is_shaper_enabled - Check LNI shaper status -+ * @lni: the give LNI -+ */ -+int qman_ceetm_lni_is_shaper_enabled(struct qm_ceetm_lni *lni); -+ -+/** -+ * qman_ceetm_lni_set_commit_rate -+ * qman_ceetm_lni_get_commit_rate -+ * qman_ceetm_lni_set_excess_rate -+ * qman_ceetm_lni_get_excess_rate - Set/get the shaper CR/ER token rate and -+ * token limit for the given LNI. -+ * @lni: the given LNI. -+ * @token_rate: the desired token rate for "set" fuction, or the token rate of -+ * the LNI queried by "get" function. -+ * @token_limit: the desired token bucket limit for "set" function, or the token -+ * limit of the given LNI queried by "get" function. -+ * -+ * Returns zero for success. The "set" function returns -EINVAL if the given -+ * LNI is unshapped or -EIO if the configure shaper command returns error. -+ * The "get" function returns -EINVAL if the token rate or the token limit is -+ * not set or the query command returns error. -+ */ -+int qman_ceetm_lni_set_commit_rate(struct qm_ceetm_lni *lni, -+ const struct qm_ceetm_rate *token_rate, -+ u16 token_limit); -+int qman_ceetm_lni_get_commit_rate(struct qm_ceetm_lni *lni, -+ struct qm_ceetm_rate *token_rate, -+ u16 *token_limit); -+int qman_ceetm_lni_set_excess_rate(struct qm_ceetm_lni *lni, -+ const struct qm_ceetm_rate *token_rate, -+ u16 token_limit); -+int qman_ceetm_lni_get_excess_rate(struct qm_ceetm_lni *lni, -+ struct qm_ceetm_rate *token_rate, -+ u16 *token_limit); -+/** -+ * qman_ceetm_lni_set_commit_rate_bps -+ * qman_ceetm_lni_get_commit_rate_bps -+ * qman_ceetm_lni_set_excess_rate_bps -+ * qman_ceetm_lni_get_excess_rate_bps - Set/get the shaper CR/ER rate -+ * and token limit for the given LNI. -+ * @lni: the given LNI. -+ * @bps: the desired shaping rate in bps for "set" fuction, or the shaping rate -+ * of the LNI queried by "get" function. -+ * @token_limit: the desired token bucket limit for "set" function, or the token -+ * limit of the given LNI queried by "get" function. -+ * -+ * Returns zero for success. The "set" function returns -EINVAL if the given -+ * LNI is unshapped or -EIO if the configure shaper command returns error. -+ * The "get" function returns -EINVAL if the token rate or the token limit is -+ * not set or the query command returns error. -+ */ -+int qman_ceetm_lni_set_commit_rate_bps(struct qm_ceetm_lni *lni, -+ u64 bps, -+ u16 token_limit); -+int qman_ceetm_lni_get_commit_rate_bps(struct qm_ceetm_lni *lni, -+ u64 *bps, u16 *token_limit); -+int qman_ceetm_lni_set_excess_rate_bps(struct qm_ceetm_lni *lni, -+ u64 bps, -+ u16 token_limit); -+int qman_ceetm_lni_get_excess_rate_bps(struct qm_ceetm_lni *lni, -+ u64 *bps, u16 *token_limit); -+ -+/** -+ * qman_ceetm_lni_set_tcfcc -+ * qman_ceetm_lni_get_tcfcc - Configure/query "Traffic Class Flow Control". -+ * @lni: the given LNI. -+ * @cq_level: is between 0 and 15, representing individual class queue levels -+ * (CQ0 to CQ7 for every channel) and grouped class queue levels (CQ8 to CQ15 -+ * for every channel). -+ * @traffic_class: is between 0 and 7 when associating a given class queue level -+ * to a traffic class, or -1 when disabling traffic class flow control for this -+ * class queue level. -+ * -+ * Return zero for success, or -EINVAL if the cq_level or traffic_class is out -+ * of range as indicated above, or -EIO if the configure/query tcfcc command -+ * returns error. -+ * -+ * Refer to the section of QMan CEETM traffic class flow control in the -+ * Reference Manual. -+ */ -+int qman_ceetm_lni_set_tcfcc(struct qm_ceetm_lni *lni, -+ unsigned int cq_level, -+ int traffic_class); -+int qman_ceetm_lni_get_tcfcc(struct qm_ceetm_lni *lni, -+ unsigned int cq_level, -+ int *traffic_class); -+ -+ /* ----------------------------- */ -+ /* CEETM :: class queue channels */ -+ /* ----------------------------- */ -+ -+/** -+ * qman_ceetm_channel_claim - Claims an unclaimed CQ channel that is mapped to -+ * the given LNI. -+ * @channel: the returned class queue channel object, if successful. -+ * @lni: the LNI that the channel belongs to. -+ * -+ * Channels are always initially "unshaped". -+ * -+ * Return zero for success, or -ENODEV if there is no channel available(all 32 -+ * channels are claimed) or -EINVAL if the channel mapping command returns -+ * error. -+ */ -+int qman_ceetm_channel_claim(struct qm_ceetm_channel **channel, -+ struct qm_ceetm_lni *lni); -+ -+/** -+ * qman_ceetm_channel_release - Releases a previously claimed CQ channel. -+ * @channel: the channel needs to be released. -+ * -+ * Returns zero for success, or -EBUSY if the dependencies are still in use. -+ * -+ * Note any shaping of the channel will be cleared to leave it in an unshaped -+ * state. -+ */ -+int qman_ceetm_channel_release(struct qm_ceetm_channel *channel); -+ -+/** -+ * qman_ceetm_channel_enable_shaper -+ * qman_ceetm_channel_disable_shaper - Enables/disables shaping on the channel. -+ * @channel: the given channel. -+ * @coupled: indicates whether surplus CR tokens should be added to the -+ * excess-rate token count (up to the excess-rate token limit) when the number -+ * of (unused) committed-rate tokens reach the committed_rate token limit. -+ * -+ * Whenever a claimed channel is first enabled for shaping, its committed and -+ * excess token rates and limits are zero, so will need to be changed to do -+ * anything useful. The shaper can subsequently be enabled/disabled without -+ * resetting the shaping parameters, but the shaping parameters will be reset -+ * when the channel is released. -+ * -+ * Return 0 for success, or -EINVAL for failure, in the case that the channel -+ * shaper has been enabled/disabled or the management command returns error. -+ */ -+int qman_ceetm_channel_enable_shaper(struct qm_ceetm_channel *channel, -+ int coupled); -+int qman_ceetm_channel_disable_shaper(struct qm_ceetm_channel *channel); -+ -+/** -+ * qman_ceetm_channel_is_shaper_enabled - Check channel shaper status. -+ * @channel: the give channel. -+ */ -+int qman_ceetm_channel_is_shaper_enabled(struct qm_ceetm_channel *channel); -+ -+/** -+ * qman_ceetm_channel_set_commit_rate -+ * qman_ceetm_channel_get_commit_rate -+ * qman_ceetm_channel_set_excess_rate -+ * qman_ceetm_channel_get_excess_rate - Set/get channel CR/ER shaper parameters. -+ * @channel: the given channel. -+ * @token_rate: the desired token rate for "set" function, or the queried token -+ * rate for "get" function. -+ * @token_limit: the desired token limit for "set" function, or the queried -+ * token limit for "get" function. -+ * -+ * Return zero for success. The "set" function returns -EINVAL if the channel -+ * is unshaped, or -EIO if the configure shapper command returns error. The -+ * "get" function returns -EINVAL if token rate of token limit is not set, or -+ * the query shaper command returns error. -+ */ -+int qman_ceetm_channel_set_commit_rate(struct qm_ceetm_channel *channel, -+ const struct qm_ceetm_rate *token_rate, -+ u16 token_limit); -+int qman_ceetm_channel_get_commit_rate(struct qm_ceetm_channel *channel, -+ struct qm_ceetm_rate *token_rate, -+ u16 *token_limit); -+int qman_ceetm_channel_set_excess_rate(struct qm_ceetm_channel *channel, -+ const struct qm_ceetm_rate *token_rate, -+ u16 token_limit); -+int qman_ceetm_channel_get_excess_rate(struct qm_ceetm_channel *channel, -+ struct qm_ceetm_rate *token_rate, -+ u16 *token_limit); -+/** -+ * qman_ceetm_channel_set_commit_rate_bps -+ * qman_ceetm_channel_get_commit_rate_bps -+ * qman_ceetm_channel_set_excess_rate_bps -+ * qman_ceetm_channel_get_excess_rate_bps - Set/get channel CR/ER shaper -+ * parameters. -+ * @channel: the given channel. -+ * @token_rate: the desired shaper rate in bps for "set" function, or the -+ * shaper rate in bps for "get" function. -+ * @token_limit: the desired token limit for "set" function, or the queried -+ * token limit for "get" function. -+ * -+ * Return zero for success. The "set" function returns -EINVAL if the channel -+ * is unshaped, or -EIO if the configure shapper command returns error. The -+ * "get" function returns -EINVAL if token rate of token limit is not set, or -+ * the query shaper command returns error. -+ */ -+int qman_ceetm_channel_set_commit_rate_bps(struct qm_ceetm_channel *channel, -+ u64 bps, u16 token_limit); -+int qman_ceetm_channel_get_commit_rate_bps(struct qm_ceetm_channel *channel, -+ u64 *bps, u16 *token_limit); -+int qman_ceetm_channel_set_excess_rate_bps(struct qm_ceetm_channel *channel, -+ u64 bps, u16 token_limit); -+int qman_ceetm_channel_get_excess_rate_bps(struct qm_ceetm_channel *channel, -+ u64 *bps, u16 *token_limit); -+ -+/** -+ * qman_ceetm_channel_set_weight -+ * qman_ceetm_channel_get_weight - Set/get the weight for unshaped channel -+ * @channel: the given channel. -+ * @token_limit: the desired token limit as the weight of the unshaped channel -+ * for "set" function, or the queried token limit for "get" function. -+ * -+ * The algorithm of unshaped fair queuing (uFQ) is used for unshaped channel. -+ * It allows the unshaped channels to be included in the CR time eligible list, -+ * and thus use the configured CR token limit value as their fair queuing -+ * weight. -+ * -+ * Return zero for success, or -EINVAL if the channel is a shaped channel or -+ * the management command returns error. -+ */ -+int qman_ceetm_channel_set_weight(struct qm_ceetm_channel *channel, -+ u16 token_limit); -+int qman_ceetm_channel_get_weight(struct qm_ceetm_channel *channel, -+ u16 *token_limit); -+ -+/** -+ * qman_ceetm_channel_set_group -+ * qman_ceetm_channel_get_group - Set/get the grouping of the class scheduler. -+ * @channel: the given channel. -+ * @group_b: indicates whether there is group B in this channel. -+ * @prio_a: the priority of group A. -+ * @prio_b: the priority of group B. -+ * -+ * There are 8 individual class queues (CQ0-CQ7), and 8 grouped class queues -+ * (CQ8-CQ15). If 'group_b' is zero, then all the grouped class queues are in -+ * group A, otherwise they are split into group A (CQ8-11) and group B -+ * (CQ12-C15). The individual class queues and the group(s) are in strict -+ * priority order relative to each other. Within the group(s), the scheduling -+ * is not strict priority order, but the result of scheduling within a group -+ * is in strict priority order relative to the other class queues in the -+ * channel. 'prio_a' and 'prio_b' control the priority order of the groups -+ * relative to the individual class queues, and take values from 0-7. Eg. if -+ * 'group_b' is non-zero, 'prio_a' is 2 and 'prio_b' is 6, then the strict -+ * priority order would be; -+ * CQ0, CQ1, CQ2, GROUPA, CQ3, CQ4, CQ5, CQ6, GROUPB, CQ7 -+ * -+ * Return 0 for success. For "set" function, returns -EINVAL if prio_a or -+ * prio_b are out of the range 0 - 7 (priority of group A or group B can not -+ * be 0, CQ0 is always the highest class queue in this channel.), or -EIO if -+ * the configure scheduler command returns error. For "get" function, return -+ * -EINVAL if the query scheduler command returns error. -+ */ -+int qman_ceetm_channel_set_group(struct qm_ceetm_channel *channel, -+ int group_b, -+ unsigned int prio_a, -+ unsigned int prio_b); -+int qman_ceetm_channel_get_group(struct qm_ceetm_channel *channel, -+ int *group_b, -+ unsigned int *prio_a, -+ unsigned int *prio_b); -+ -+/** -+ * qman_ceetm_channel_set_group_cr_eligibility -+ * qman_ceetm_channel_set_group_er_eligibility - Set channel group eligibility -+ * @channel: the given channel object -+ * @group_b: indicates whether there is group B in this channel. -+ * @cre: the commit rate eligibility, 1 for enable, 0 for disable. -+ * -+ * Return zero for success, or -EINVAL if eligibility setting fails. -+*/ -+int qman_ceetm_channel_set_group_cr_eligibility(struct qm_ceetm_channel -+ *channel, int group_b, int cre); -+int qman_ceetm_channel_set_group_er_eligibility(struct qm_ceetm_channel -+ *channel, int group_b, int ere); -+ -+/** -+ * qman_ceetm_channel_set_cq_cr_eligibility -+ * qman_ceetm_channel_set_cq_er_eligibility - Set channel cq eligibility -+ * @channel: the given channel object -+ * @idx: is from 0 to 7 (representing CQ0 to CQ7). -+ * @cre: the commit rate eligibility, 1 for enable, 0 for disable. -+ * -+ * Return zero for success, or -EINVAL if eligibility setting fails. -+*/ -+int qman_ceetm_channel_set_cq_cr_eligibility(struct qm_ceetm_channel *channel, -+ unsigned int idx, int cre); -+int qman_ceetm_channel_set_cq_er_eligibility(struct qm_ceetm_channel *channel, -+ unsigned int idx, int ere); -+ -+ /* --------------------- */ -+ /* CEETM :: class queues */ -+ /* --------------------- */ -+ -+/** -+ * qman_ceetm_cq_claim - Claims an individual class queue. -+ * @cq: the returned class queue object, if successful. -+ * @channel: the class queue channel. -+ * @idx: is from 0 to 7 (representing CQ0 to CQ7). -+ * @ccg: represents the class congestion group that this class queue should be -+ * subscribed to, or NULL if no congestion group membership is desired. -+ * -+ * Returns zero for success, or -EINVAL if @idx is out of range 0 - 7 or -+ * if this class queue has been claimed, or configure class queue command -+ * returns error, or returns -ENOMEM if allocating CQ memory fails. -+ */ -+int qman_ceetm_cq_claim(struct qm_ceetm_cq **cq, -+ struct qm_ceetm_channel *channel, -+ unsigned int idx, -+ struct qm_ceetm_ccg *ccg); -+ -+/** -+ * qman_ceetm_cq_claim_A - Claims a class queue group A. -+ * @cq: the returned class queue object, if successful. -+ * @channel: the class queue channel. -+ * @idx: is from 8 to 15 if only group A exits, otherwise, it is from 8 to 11. -+ * @ccg: represents the class congestion group that this class queue should be -+ * subscribed to, or NULL if no congestion group membership is desired. -+ * -+ * Return zero for success, or -EINVAL if @idx is out the range or if -+ * this class queue has been claimed or configure class queue command returns -+ * error, or returns -ENOMEM if allocating CQ memory fails. -+ */ -+int qman_ceetm_cq_claim_A(struct qm_ceetm_cq **cq, -+ struct qm_ceetm_channel *channel, -+ unsigned int idx, -+ struct qm_ceetm_ccg *ccg); -+ -+/** -+ * qman_ceetm_cq_claim_B - Claims a class queue group B. -+ * @cq: the returned class queue object, if successful. -+ * @channel: the class queue channel. -+ * @idx: is from 0 to 3 (CQ12 to CQ15). -+ * @ccg: represents the class congestion group that this class queue should be -+ * subscribed to, or NULL if no congestion group membership is desired. -+ * -+ * Return zero for success, or -EINVAL if @idx is out the range or if -+ * this class queue has been claimed or configure class queue command returns -+ * error, or returns -ENOMEM if allocating CQ memory fails. -+ */ -+int qman_ceetm_cq_claim_B(struct qm_ceetm_cq **cq, -+ struct qm_ceetm_channel *channel, -+ unsigned int idx, -+ struct qm_ceetm_ccg *ccg); -+ -+/** -+ * qman_ceetm_cq_release - Releases a previously claimed class queue. -+ * @cq: The class queue to be released. -+ * -+ * Return zero for success, or -EBUSY if the dependent objects (eg. logical -+ * FQIDs) have not been released. -+ */ -+int qman_ceetm_cq_release(struct qm_ceetm_cq *cq); -+ -+/** -+ * qman_ceetm_set_queue_weight -+ * qman_ceetm_get_queue_weight - Configure/query the weight of a grouped class -+ * queue. -+ * @cq: the given class queue. -+ * @weight_code: the desired weight code to set for the given class queue for -+ * "set" function or the queired weight code for "get" function. -+ * -+ * Grouped class queues have a default weight code of zero, which corresponds to -+ * a scheduler weighting of 1. This function can be used to modify a grouped -+ * class queue to another weight, (Use the helpers qman_ceetm_wbfs2ratio() -+ * and qman_ceetm_ratio2wbfs() to convert between these 'weight_code' values -+ * and the corresponding sharing weight.) -+ * -+ * Returns zero for success, or -EIO if the configure weight command returns -+ * error for "set" function, or -EINVAL if the query command returns -+ * error for "get" function. -+ * See section "CEETM Weighted Scheduling among Grouped Classes" in Reference -+ * Manual for weight and weight code. -+ */ -+int qman_ceetm_set_queue_weight(struct qm_ceetm_cq *cq, -+ struct qm_ceetm_weight_code *weight_code); -+int qman_ceetm_get_queue_weight(struct qm_ceetm_cq *cq, -+ struct qm_ceetm_weight_code *weight_code); -+ -+/** -+ * qman_ceetm_set_queue_weight_in_ratio -+ * qman_ceetm_get_queue_weight_in_ratio - Configure/query the weight of a -+ * grouped class queue. -+ * @cq: the given class queue. -+ * @ratio: the weight in ratio. It should be the real ratio number multiplied -+ * by 100 to get rid of fraction. -+ * -+ * Returns zero for success, or -EIO if the configure weight command returns -+ * error for "set" function, or -EINVAL if the query command returns -+ * error for "get" function. -+ */ -+int qman_ceetm_set_queue_weight_in_ratio(struct qm_ceetm_cq *cq, u32 ratio); -+int qman_ceetm_get_queue_weight_in_ratio(struct qm_ceetm_cq *cq, u32 *ratio); -+ -+/* Weights are encoded using a pseudo-exponential scheme. The weight codes 0, -+ * 32, 64, [...] correspond to weights of 1, 2, 4, [...]. The weights -+ * corresponding to intermediate weight codes are calculated using linear -+ * interpolation on the inverted values. Or put another way, the inverse weights -+ * for each 32nd weight code are 1, 1/2, 1/4, [...], and so the intervals -+ * between these are divided linearly into 32 intermediate values, the inverses -+ * of which form the remaining weight codes. -+ * -+ * The Weighted Bandwidth Fair Scheduling (WBFS) algorithm provides a form of -+ * scheduling within a group of class queues (group A or B). Weights are used to -+ * normalise the class queues to an underlying BFS algorithm where all class -+ * queues are assumed to require "equal bandwidth". So the weights referred to -+ * by the weight codes act as divisors on the size of frames being enqueued. Ie. -+ * one class queue in a group is assigned a weight of 2 whilst the other class -+ * queues in the group keep the default weight of 1, then the WBFS scheduler -+ * will effectively treat all frames enqueued on the weight-2 class queue as -+ * having half the number of bytes they really have. Ie. if all other things are -+ * equal, that class queue would get twice as much bytes-per-second bandwidth as -+ * the others. So weights should be chosen to provide bandwidth ratios between -+ * members of the same class queue group. These weights have no bearing on -+ * behaviour outside that group's WBFS mechanism though. -+ */ -+ -+/** -+ * qman_ceetm_wbfs2ratio - Given a weight code ('wbfs'), an accurate fractional -+ * representation of the corresponding weight is given (in order to not lose -+ * any precision). -+ * @weight_code: The given weight code in WBFS. -+ * @numerator: the numerator part of the weight computed by the weight code. -+ * @denominator: the denominator part of the weight computed by the weight code -+ * -+ * Returns zero for success or -EINVAL if the given weight code is illegal. -+ */ -+int qman_ceetm_wbfs2ratio(struct qm_ceetm_weight_code *weight_code, -+ u32 *numerator, -+ u32 *denominator); -+/** -+ * qman_ceetm_ratio2wbfs - Given a weight, find the nearest possible weight code -+ * If the user needs to know how close this is, convert the resulting weight -+ * code back to a weight and compare. -+ * @numerator: numerator part of the given weight. -+ * @denominator: denominator part of the given weight. -+ * @weight_code: the weight code computed from the given weight. -+ * -+ * Returns zero for success, or -ERANGE if "numerator/denominator" is outside -+ * the range of weights. -+ */ -+int qman_ceetm_ratio2wbfs(u32 numerator, -+ u32 denominator, -+ struct qm_ceetm_weight_code *weight_code, -+ int rounding); -+ -+#define QMAN_CEETM_FLAG_CLEAR_STATISTICS_COUNTER 0x1 -+/** -+ * qman_ceetm_cq_get_dequeue_statistics - Get the statistics provided by CEETM -+ * CQ counters. -+ * @cq: the given CQ object. -+ * @flags: indicates whether the statistics counter will be cleared after query. -+ * @frame_count: The number of the frames that have been counted since the -+ * counter was cleared last time. -+ * @byte_count: the number of bytes in all frames that have been counted. -+ * -+ * Return zero for success or -EINVAL if query statistics command returns error. -+ * -+ */ -+int qman_ceetm_cq_get_dequeue_statistics(struct qm_ceetm_cq *cq, u32 flags, -+ u64 *frame_count, u64 *byte_count); -+ -+/** -+ * qman_ceetm_drain_cq - drain the CQ till it is empty. -+ * @cq: the give CQ object. -+ * Return 0 for success or -EINVAL for unsuccessful command to empty CQ. -+ */ -+int qman_ceetm_drain_cq(struct qm_ceetm_cq *cq); -+ -+ /* ---------------------- */ -+ /* CEETM :: logical FQIDs */ -+ /* ---------------------- */ -+/** -+ * qman_ceetm_lfq_claim - Claims an unused logical FQID, associates it with -+ * the given class queue. -+ * @lfq: the returned lfq object, if successful. -+ * @cq: the class queue which needs to claim a LFQID. -+ * -+ * Return zero for success, or -ENODEV if no LFQID is available or -ENOMEM if -+ * allocating memory for lfq fails, or -EINVAL if configuring LFQMT fails. -+ */ -+int qman_ceetm_lfq_claim(struct qm_ceetm_lfq **lfq, -+ struct qm_ceetm_cq *cq); -+ -+/** -+ * qman_ceetm_lfq_release - Releases a previously claimed logical FQID. -+ * @lfq: the lfq to be released. -+ * -+ * Return zero for success. -+ */ -+int qman_ceetm_lfq_release(struct qm_ceetm_lfq *lfq); -+ -+/** -+ * qman_ceetm_lfq_set_context -+ * qman_ceetm_lfq_get_context - Set/get the context_a/context_b pair to the -+ * "dequeue context table" associated with the logical FQID. -+ * @lfq: the given logical FQ object. -+ * @context_a: contextA of the dequeue context. -+ * @context_b: contextB of the dequeue context. -+ * -+ * Returns zero for success, or -EINVAL if there is error to set/get the -+ * context pair. -+ */ -+int qman_ceetm_lfq_set_context(struct qm_ceetm_lfq *lfq, -+ u64 context_a, -+ u32 context_b); -+int qman_ceetm_lfq_get_context(struct qm_ceetm_lfq *lfq, -+ u64 *context_a, -+ u32 *context_b); -+ -+/** -+ * qman_ceetm_create_fq - Initialise a FQ object for the LFQ. -+ * @lfq: the given logic fq. -+ * @fq: the fq object created for the given logic fq. -+ * -+ * The FQ object can be used in qman_enqueue() and qman_enqueue_orp() APIs to -+ * target a logical FQID (and the class queue it is associated with). -+ * Note that this FQ object can only be used for enqueues, and -+ * in the case of qman_enqueue_orp() it can not be used as the 'orp' parameter, -+ * only as 'fq'. This FQ object can not (and shouldn't) be destroyed, it is only -+ * valid as long as the underlying 'lfq' remains claimed. It is the user's -+ * responsibility to ensure that the underlying 'lfq' is not released until any -+ * enqueues to this FQ object have completed. The only field the user needs to -+ * fill in is fq->cb.ern, as that enqueue rejection handler is the callback that -+ * could conceivably be called on this FQ object. This API can be called -+ * multiple times to create multiple FQ objects referring to the same logical -+ * FQID, and any enqueue rejections will respect the callback of the object that -+ * issued the enqueue (and will identify the object via the parameter passed to -+ * the callback too). There is no 'flags' parameter to this API as there is for -+ * qman_create_fq() - the created FQ object behaves as though qman_create_fq() -+ * had been called with the single flag QMAN_FQ_FLAG_NO_MODIFY. -+ * -+ * Returns 0 for success. -+ */ -+int qman_ceetm_create_fq(struct qm_ceetm_lfq *lfq, struct qman_fq *fq); -+ -+ /* -------------------------------- */ -+ /* CEETM :: class congestion groups */ -+ /* -------------------------------- */ -+ -+/** -+ * qman_ceetm_ccg_claim - Claims an unused CCG. -+ * @ccg: the returned CCG object, if successful. -+ * @channel: the given class queue channel -+ * @cscn: the callback function of this CCG. -+ * @cb_ctx: the corresponding context to be used used if state change -+ * notifications are later enabled for this CCG. -+ * -+ * The congestion group is local to the given class queue channel, so only -+ * class queues within the channel can be associated with that congestion group. -+ * The association of class queues to congestion groups occurs when the class -+ * queues are claimed, see qman_ceetm_cq_claim() and related functions. -+ * Congestion groups are in a "zero" state when initially claimed, and they are -+ * returned to that state when released. -+ * -+ * Return zero for success, or -EINVAL if no CCG in the channel is available. -+ */ -+int qman_ceetm_ccg_claim(struct qm_ceetm_ccg **ccg, -+ struct qm_ceetm_channel *channel, -+ unsigned int idx, -+ void (*cscn)(struct qm_ceetm_ccg *, -+ void *cb_ctx, -+ int congested), -+ void *cb_ctx); -+ -+/** -+ * qman_ceetm_ccg_release - Releases a previously claimed CCG. -+ * @ccg: the given ccg. -+ * -+ * Returns zero for success, or -EBUSY if the given ccg's dependent objects -+ * (class queues that are associated with the CCG) have not been released. -+ */ -+int qman_ceetm_ccg_release(struct qm_ceetm_ccg *ccg); -+ -+/* This struct is used to specify attributes for a CCG. The 'we_mask' field -+ * controls which CCG attributes are to be updated, and the remainder specify -+ * the values for those attributes. A CCG counts either frames or the bytes -+ * within those frames, but not both ('mode'). A CCG can optionally cause -+ * enqueues to be rejected, due to tail-drop or WRED, or both (they are -+ * independent options, 'td_en' and 'wr_en_g,wr_en_y,wr_en_r'). Tail-drop can be -+ * level-triggered due to a single threshold ('td_thres') or edge-triggered due -+ * to a "congestion state", but not both ('td_mode'). Congestion state has -+ * distinct entry and exit thresholds ('cs_thres_in' and 'cs_thres_out'), and -+ * notifications can be sent to software the CCG goes in to and out of this -+ * congested state ('cscn_en'). */ -+struct qm_ceetm_ccg_params { -+ /* Boolean fields together in a single bitfield struct */ -+ struct { -+ /* Whether to count bytes or frames. 1==frames */ -+ u8 mode:1; -+ /* En/disable tail-drop. 1==enable */ -+ u8 td_en:1; -+ /* Tail-drop on congestion-state or threshold. 1=threshold */ -+ u8 td_mode:1; -+ /* Generate congestion state change notifications. 1==enable */ -+ u8 cscn_en:1; -+ /* Enable WRED rejections (per colour). 1==enable */ -+ u8 wr_en_g:1; -+ u8 wr_en_y:1; -+ u8 wr_en_r:1; -+ } __packed; -+ /* Tail-drop threshold. See qm_cgr_thres_[gs]et64(). */ -+ struct qm_cgr_cs_thres td_thres; -+ /* Congestion state thresholds, for entry and exit. */ -+ struct qm_cgr_cs_thres cs_thres_in; -+ struct qm_cgr_cs_thres cs_thres_out; -+ /* Overhead accounting length. Per-packet "tax", from -128 to +127 */ -+ signed char oal; -+ /* Congestion state change notification for DCP portal, virtual CCGID*/ -+ /* WRED parameters. */ -+ struct qm_cgr_wr_parm wr_parm_g; -+ struct qm_cgr_wr_parm wr_parm_y; -+ struct qm_cgr_wr_parm wr_parm_r; -+}; -+/* Bits used in 'we_mask' to qman_ceetm_ccg_set(), controls which attributes of -+ * the CCGR are to be updated. */ -+#define QM_CCGR_WE_MODE 0x0001 /* mode (bytes/frames) */ -+#define QM_CCGR_WE_CS_THRES_IN 0x0002 /* congestion state entry threshold */ -+#define QM_CCGR_WE_TD_EN 0x0004 /* congestion state tail-drop enable */ -+#define QM_CCGR_WE_CSCN_TUPD 0x0008 /* CSCN target update */ -+#define QM_CCGR_WE_CSCN_EN 0x0010 /* congestion notification enable */ -+#define QM_CCGR_WE_WR_EN_R 0x0020 /* WRED enable - red */ -+#define QM_CCGR_WE_WR_EN_Y 0x0040 /* WRED enable - yellow */ -+#define QM_CCGR_WE_WR_EN_G 0x0080 /* WRED enable - green */ -+#define QM_CCGR_WE_WR_PARM_R 0x0100 /* WRED parameters - red */ -+#define QM_CCGR_WE_WR_PARM_Y 0x0200 /* WRED parameters - yellow */ -+#define QM_CCGR_WE_WR_PARM_G 0x0400 /* WRED parameters - green */ -+#define QM_CCGR_WE_OAL 0x0800 /* overhead accounting length */ -+#define QM_CCGR_WE_CS_THRES_OUT 0x1000 /* congestion state exit threshold */ -+#define QM_CCGR_WE_TD_THRES 0x2000 /* tail-drop threshold */ -+#define QM_CCGR_WE_TD_MODE 0x4000 /* tail-drop mode (state/threshold) */ -+#define QM_CCGR_WE_CDV 0x8000 /* cdv */ -+ -+/** -+ * qman_ceetm_ccg_set -+ * qman_ceetm_ccg_get - Configure/query a subset of CCG attributes. -+ * @ccg: the given CCG object. -+ * @we_mask: the write enable mask. -+ * @params: the parameters setting for this ccg -+ * -+ * Return 0 for success, or -EIO if configure ccg command returns error for -+ * "set" function, or -EINVAL if query ccg command returns error for "get" -+ * function. -+ */ -+int qman_ceetm_ccg_set(struct qm_ceetm_ccg *ccg, -+ u16 we_mask, -+ const struct qm_ceetm_ccg_params *params); -+int qman_ceetm_ccg_get(struct qm_ceetm_ccg *ccg, -+ struct qm_ceetm_ccg_params *params); -+ -+/** qman_ceetm_cscn_swp_set - Add or remove a software portal from the target -+ * mask. -+ * qman_ceetm_cscn_swp_get - Query whether a given software portal index is -+ * in the cscn target mask. -+ * @ccg: the give CCG object. -+ * @swp_idx: the index of the software portal. -+ * @cscn_enabled: 1: Set the swp to be cscn target. 0: remove the swp from -+ * the target mask. -+ * @we_mask: the write enable mask. -+ * @params: the parameters setting for this ccg -+ * -+ * Return 0 for success, or -EINVAL if command in set/get function fails. -+ */ -+int qman_ceetm_cscn_swp_set(struct qm_ceetm_ccg *ccg, -+ u16 swp_idx, -+ unsigned int cscn_enabled, -+ u16 we_mask, -+ const struct qm_ceetm_ccg_params *params); -+int qman_ceetm_cscn_swp_get(struct qm_ceetm_ccg *ccg, -+ u16 swp_idx, -+ unsigned int *cscn_enabled); -+ -+/** qman_ceetm_cscn_dcp_set - Add or remove a direct connect portal from the\ -+ * target mask. -+ * qman_ceetm_cscn_dcp_get - Query whether a given direct connect portal index -+ * is in the cscn target mask. -+ * @ccg: the give CCG object. -+ * @dcp_idx: the index of the direct connect portal. -+ * @vcgid: congestion state change notification for dcp portal, virtual CGID. -+ * @cscn_enabled: 1: Set the dcp to be cscn target. 0: remove the dcp from -+ * the target mask. -+ * @we_mask: the write enable mask. -+ * @params: the parameters setting for this ccg -+ * -+ * Return 0 for success, or -EINVAL if command in set/get function fails. -+ */ -+int qman_ceetm_cscn_dcp_set(struct qm_ceetm_ccg *ccg, -+ u16 dcp_idx, -+ u8 vcgid, -+ unsigned int cscn_enabled, -+ u16 we_mask, -+ const struct qm_ceetm_ccg_params *params); -+int qman_ceetm_cscn_dcp_get(struct qm_ceetm_ccg *ccg, -+ u16 dcp_idx, -+ u8 *vcgid, -+ unsigned int *cscn_enabled); -+ -+/** -+ * qman_ceetm_ccg_get_reject_statistics - Get the statistics provided by -+ * CEETM CCG counters. -+ * @ccg: the given CCG object. -+ * @flags: indicates whether the statistics counter will be cleared after query. -+ * @frame_count: The number of the frames that have been counted since the -+ * counter was cleared last time. -+ * @byte_count: the number of bytes in all frames that have been counted. -+ * -+ * Return zero for success or -EINVAL if query statistics command returns error. -+ * -+ */ -+int qman_ceetm_ccg_get_reject_statistics(struct qm_ceetm_ccg *ccg, u32 flags, -+ u64 *frame_count, u64 *byte_count); -+ -+/** -+ * qman_ceetm_query_lfqmt - Query the logical frame queue mapping table -+ * @lfqid: Logical Frame Queue ID -+ * @lfqmt_query: Results of the query command -+ * -+ * Returns zero for success or -EIO if the query command returns error. -+ * -+ */ -+int qman_ceetm_query_lfqmt(int lfqid, -+ struct qm_mcr_ceetm_lfqmt_query *lfqmt_query); -+ -+/** -+ * qman_ceetm_query_write_statistics - Query (and optionally write) statistics -+ * @cid: Target ID (CQID or CCGRID) -+ * @dcp_idx: CEETM portal ID -+ * @command_type: One of the following: -+ * 0 = Query dequeue statistics. CID carries the CQID to be queried. -+ * 1 = Query and clear dequeue statistics. CID carries the CQID to be queried -+ * 2 = Write dequeue statistics. CID carries the CQID to be written. -+ * 3 = Query reject statistics. CID carries the CCGRID to be queried. -+ * 4 = Query and clear reject statistics. CID carries the CCGRID to be queried -+ * 5 = Write reject statistics. CID carries the CCGRID to be written -+ * @frame_count: Frame count value to be written if this is a write command -+ * @byte_count: Bytes count value to be written if this is a write command -+ * -+ * Returns zero for success or -EIO if the query command returns error. -+ */ -+int qman_ceetm_query_write_statistics(u16 cid, enum qm_dc_portal dcp_idx, -+ u16 command_type, u64 frame_count, -+ u64 byte_count); -+ -+/** -+ * qman_set_wpm - Set waterfall power management -+ * -+ * @wpm_enable: boolean, 1 = enable wpm, 0 = disable wpm. -+ * -+ * Return 0 for success, return -ENODEV if QMan misc_cfg register is not -+ * accessible. -+ */ -+int qman_set_wpm(int wpm_enable); -+ -+/** -+ * qman_get_wpm - Query the waterfall power management setting -+ * -+ * @wpm_enable: boolean, 1 = enable wpm, 0 = disable wpm. -+ * -+ * Return 0 for success, return -ENODEV if QMan misc_cfg register is not -+ * accessible. -+ */ -+int qman_get_wpm(int *wpm_enable); -+ -+/* The below qman_p_***() variants might be called in a migration situation -+ * (e.g. cpu hotplug). They are used to continue accessing the portal that -+ * execution was affine to prior to migration. -+ * @qman_portal specifies which portal the APIs will use. -+*/ -+const struct qman_portal_config *qman_p_get_portal_config(struct qman_portal -+ *p); -+int qman_p_irqsource_add(struct qman_portal *p, u32 bits); -+int qman_p_irqsource_remove(struct qman_portal *p, u32 bits); -+int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit); -+u32 qman_p_poll_slow(struct qman_portal *p); -+void qman_p_poll(struct qman_portal *p); -+void qman_p_stop_dequeues(struct qman_portal *p); -+void qman_p_start_dequeues(struct qman_portal *p); -+void qman_p_static_dequeue_add(struct qman_portal *p, u32 pools); -+void qman_p_static_dequeue_del(struct qman_portal *p, u32 pools); -+u32 qman_p_static_dequeue_get(struct qman_portal *p); -+void qman_p_dca(struct qman_portal *p, struct qm_dqrr_entry *dq, -+ int park_request); -+int qman_p_volatile_dequeue(struct qman_portal *p, struct qman_fq *fq, -+ u32 flags __maybe_unused, u32 vdqcr); -+int qman_p_enqueue(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_fd *fd, u32 flags); -+int qman_p_enqueue_orp(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_fd *fd, u32 flags, -+ struct qman_fq *orp, u16 orp_seqnum); -+int qman_p_enqueue_precommit(struct qman_portal *p, struct qman_fq *fq, -+ const struct qm_fd *fd, u32 flags, -+ qman_cb_precommit cb, void *cb_arg); -+#ifdef __cplusplus -+} -+#endif -+ -+#endif /* FSL_QMAN_H */ ---- /dev/null -+++ b/include/linux/fsl_usdpaa.h -@@ -0,0 +1,372 @@ -+/* Copyright 2011-2012 Freescale Semiconductor, Inc. -+ * -+ * This file is licensed under the terms of the GNU General Public License -+ * version 2. This program is licensed "as is" without any warranty of any -+ * kind, whether express or implied. -+ */ -+ -+#ifndef FSL_USDPAA_H -+#define FSL_USDPAA_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#include <linux/uaccess.h> -+#include <linux/ioctl.h> -+#include <linux/fsl_qman.h> /* For "enum qm_channel" */ -+#include <linux/compat.h> -+ -+#ifdef CONFIG_FSL_USDPAA -+ -+/******************************/ -+/* Allocation of resource IDs */ -+/******************************/ -+ -+/* This enum is used to distinguish between the type of underlying object being -+ * manipulated. */ -+enum usdpaa_id_type { -+ usdpaa_id_fqid, -+ usdpaa_id_bpid, -+ usdpaa_id_qpool, -+ usdpaa_id_cgrid, -+ usdpaa_id_ceetm0_lfqid, -+ usdpaa_id_ceetm0_channelid, -+ usdpaa_id_ceetm1_lfqid, -+ usdpaa_id_ceetm1_channelid, -+ usdpaa_id_max /* <-- not a valid type, represents the number of types */ -+}; -+#define USDPAA_IOCTL_MAGIC 'u' -+struct usdpaa_ioctl_id_alloc { -+ uint32_t base; /* Return value, the start of the allocated range */ -+ enum usdpaa_id_type id_type; /* what kind of resource(s) to allocate */ -+ uint32_t num; /* how many IDs to allocate (and return value) */ -+ uint32_t align; /* must be a power of 2, 0 is treated like 1 */ -+ int partial; /* whether to allow less than 'num' */ -+}; -+struct usdpaa_ioctl_id_release { -+ /* Input; */ -+ enum usdpaa_id_type id_type; -+ uint32_t base; -+ uint32_t num; -+}; -+struct usdpaa_ioctl_id_reserve { -+ enum usdpaa_id_type id_type; -+ uint32_t base; -+ uint32_t num; -+}; -+ -+ -+/* ioctl() commands */ -+#define USDPAA_IOCTL_ID_ALLOC \ -+ _IOWR(USDPAA_IOCTL_MAGIC, 0x01, struct usdpaa_ioctl_id_alloc) -+#define USDPAA_IOCTL_ID_RELEASE \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x02, struct usdpaa_ioctl_id_release) -+#define USDPAA_IOCTL_ID_RESERVE \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x0A, struct usdpaa_ioctl_id_reserve) -+ -+/**********************/ -+/* Mapping DMA memory */ -+/**********************/ -+ -+/* Maximum length for a map name, including NULL-terminator */ -+#define USDPAA_DMA_NAME_MAX 16 -+/* Flags for requesting DMA maps. Maps are private+unnamed or sharable+named. -+ * For a sharable and named map, specify _SHARED (whether creating one or -+ * binding to an existing one). If _SHARED is specified and _CREATE is not, then -+ * the mapping must already exist. If _SHARED and _CREATE are specified and the -+ * mapping doesn't already exist, it will be created. If _SHARED and _CREATE are -+ * specified and the mapping already exists, the mapping will fail unless _LAZY -+ * is specified. When mapping to a pre-existing sharable map, the length must be -+ * an exact match. Lengths must be a power-of-4 multiple of page size. -+ * -+ * Note that this does not actually map the memory to user-space, that is done -+ * by a subsequent mmap() using the page offset returned from this ioctl(). The -+ * ioctl() is what gives the process permission to do this, and a page-offset -+ * with which to do so. -+ */ -+#define USDPAA_DMA_FLAG_SHARE 0x01 -+#define USDPAA_DMA_FLAG_CREATE 0x02 -+#define USDPAA_DMA_FLAG_LAZY 0x04 -+#define USDPAA_DMA_FLAG_RDONLY 0x08 -+struct usdpaa_ioctl_dma_map { -+ /* Output parameters - virtual and physical addresses */ -+ void *ptr; -+ uint64_t phys_addr; -+ /* Input parameter, the length of the region to be created (or if -+ * mapping an existing region, this must match it). Must be a power-of-4 -+ * multiple of page size. */ -+ uint64_t len; -+ /* Input parameter, the USDPAA_DMA_FLAG_* settings. */ -+ uint32_t flags; -+ /* If _FLAG_SHARE is specified, the name of the region to be created (or -+ * of the existing mapping to use). */ -+ char name[USDPAA_DMA_NAME_MAX]; -+ /* If this ioctl() creates the mapping, this is an input parameter -+ * stating whether the region supports locking. If mapping an existing -+ * region, this is a return value indicating the same thing. */ -+ int has_locking; -+ /* In the case of a successful map with _CREATE and _LAZY, this return -+ * value indicates whether we created the mapped region or whether it -+ * already existed. */ -+ int did_create; -+}; -+ -+#ifdef CONFIG_COMPAT -+struct usdpaa_ioctl_dma_map_compat { -+ /* Output parameters - virtual and physical addresses */ -+ compat_uptr_t ptr; -+ uint64_t phys_addr; -+ /* Input parameter, the length of the region to be created (or if -+ * mapping an existing region, this must match it). Must be a power-of-4 -+ * multiple of page size. */ -+ uint64_t len; -+ /* Input parameter, the USDPAA_DMA_FLAG_* settings. */ -+ uint32_t flags; -+ /* If _FLAG_SHARE is specified, the name of the region to be created (or -+ * of the existing mapping to use). */ -+ char name[USDPAA_DMA_NAME_MAX]; -+ /* If this ioctl() creates the mapping, this is an input parameter -+ * stating whether the region supports locking. If mapping an existing -+ * region, this is a return value indicating the same thing. */ -+ int has_locking; -+ /* In the case of a successful map with _CREATE and _LAZY, this return -+ * value indicates whether we created the mapped region or whether it -+ * already existed. */ -+ int did_create; -+}; -+ -+#define USDPAA_IOCTL_DMA_MAP_COMPAT \ -+ _IOWR(USDPAA_IOCTL_MAGIC, 0x03, struct usdpaa_ioctl_dma_map_compat) -+#endif -+ -+ -+#define USDPAA_IOCTL_DMA_MAP \ -+ _IOWR(USDPAA_IOCTL_MAGIC, 0x03, struct usdpaa_ioctl_dma_map) -+/* munmap() does not remove the DMA map, just the user-space mapping to it. -+ * This ioctl will do both (though you can munmap() before calling the ioctl -+ * too). */ -+#define USDPAA_IOCTL_DMA_UNMAP \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x04, unsigned char) -+/* We implement a cross-process locking scheme per DMA map. Call this ioctl() -+ * with a mmap()'d address, and the process will (interruptible) sleep if the -+ * lock is already held by another process. Process destruction will -+ * automatically clean up any held locks. */ -+#define USDPAA_IOCTL_DMA_LOCK \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x05, unsigned char) -+#define USDPAA_IOCTL_DMA_UNLOCK \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x06, unsigned char) -+ -+/***************************************/ -+/* Mapping and using QMan/BMan portals */ -+/***************************************/ -+enum usdpaa_portal_type { -+ usdpaa_portal_qman, -+ usdpaa_portal_bman, -+}; -+ -+#define QBMAN_ANY_PORTAL_IDX 0xffffffff -+ -+struct usdpaa_ioctl_portal_map { -+ /* Input parameter, is a qman or bman portal required. */ -+ -+ enum usdpaa_portal_type type; -+ /* Specifes a specific portal index to map or QBMAN_ANY_PORTAL_IDX -+ for don't care. The portal index will be populated by the -+ driver when the ioctl() successfully completes */ -+ uint32_t index; -+ -+ /* Return value if the map succeeds, this gives the mapped -+ * cache-inhibited (cinh) and cache-enabled (cena) addresses. */ -+ struct usdpaa_portal_map { -+ void *cinh; -+ void *cena; -+ } addr; -+ /* Qman-specific return values */ -+ uint16_t channel; -+ uint32_t pools; -+}; -+ -+#ifdef CONFIG_COMPAT -+struct compat_usdpaa_ioctl_portal_map { -+ /* Input parameter, is a qman or bman portal required. */ -+ enum usdpaa_portal_type type; -+ /* Specifes a specific portal index to map or QBMAN_ANY_PORTAL_IDX -+ for don't care. The portal index will be populated by the -+ driver when the ioctl() successfully completes */ -+ uint32_t index; -+ /* Return value if the map succeeds, this gives the mapped -+ * cache-inhibited (cinh) and cache-enabled (cena) addresses. */ -+ struct usdpaa_portal_map_compat { -+ compat_uptr_t cinh; -+ compat_uptr_t cena; -+ } addr; -+ /* Qman-specific return values */ -+ uint16_t channel; -+ uint32_t pools; -+}; -+#define USDPAA_IOCTL_PORTAL_MAP_COMPAT \ -+ _IOWR(USDPAA_IOCTL_MAGIC, 0x07, struct compat_usdpaa_ioctl_portal_map) -+#define USDPAA_IOCTL_PORTAL_UNMAP_COMPAT \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x08, struct usdpaa_portal_map_compat) -+#endif -+ -+#define USDPAA_IOCTL_PORTAL_MAP \ -+ _IOWR(USDPAA_IOCTL_MAGIC, 0x07, struct usdpaa_ioctl_portal_map) -+#define USDPAA_IOCTL_PORTAL_UNMAP \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x08, struct usdpaa_portal_map) -+ -+struct usdpaa_ioctl_irq_map { -+ enum usdpaa_portal_type type; /* Type of portal to map */ -+ int fd; /* File descriptor that contains the portal */ -+ void *portal_cinh; /* Cache inhibited area to identify the portal */ -+}; -+ -+#define USDPAA_IOCTL_PORTAL_IRQ_MAP \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x09, struct usdpaa_ioctl_irq_map) -+ -+#ifdef CONFIG_COMPAT -+ -+struct compat_ioctl_irq_map { -+ enum usdpaa_portal_type type; /* Type of portal to map */ -+ compat_int_t fd; /* File descriptor that contains the portal */ -+ compat_uptr_t portal_cinh; /* Used identify the portal */}; -+ -+#define USDPAA_IOCTL_PORTAL_IRQ_MAP_COMPAT \ -+ _IOW(USDPAA_IOCTL_MAGIC, 0x09, struct compat_ioctl_irq_map) -+#endif -+ -+/* ioctl to query the amount of DMA memory used in the system */ -+struct usdpaa_ioctl_dma_used { -+ uint64_t free_bytes; -+ uint64_t total_bytes; -+}; -+#define USDPAA_IOCTL_DMA_USED \ -+ _IOR(USDPAA_IOCTL_MAGIC, 0x0B, struct usdpaa_ioctl_dma_used) -+ -+/* ioctl to allocate a raw portal */ -+struct usdpaa_ioctl_raw_portal { -+ /* inputs */ -+ enum usdpaa_portal_type type; /* Type of portal to allocate */ -+ -+ /* set to non zero to turn on stashing */ -+ uint8_t enable_stash; -+ /* Stashing attributes for the portal */ -+ uint32_t cpu; -+ uint32_t cache; -+ uint32_t window; -+ -+ /* Specifies the stash request queue this portal should use */ -+ uint8_t sdest; -+ -+ /* Specifes a specific portal index to map or QBMAN_ANY_PORTAL_IDX -+ * for don't care. The portal index will be populated by the -+ * driver when the ioctl() successfully completes */ -+ uint32_t index; -+ -+ /* outputs */ -+ uint64_t cinh; -+ uint64_t cena; -+}; -+ -+#define USDPAA_IOCTL_ALLOC_RAW_PORTAL \ -+ _IOWR(USDPAA_IOCTL_MAGIC, 0x0C, struct usdpaa_ioctl_raw_portal) -+ -+#define USDPAA_IOCTL_FREE_RAW_PORTAL \ -+ _IOR(USDPAA_IOCTL_MAGIC, 0x0D, struct usdpaa_ioctl_raw_portal) -+ -+#ifdef CONFIG_COMPAT -+ -+struct compat_ioctl_raw_portal { -+ /* inputs */ -+ enum usdpaa_portal_type type; /* Type of portal to allocate */ -+ -+ /* set to non zero to turn on stashing */ -+ uint8_t enable_stash; -+ /* Stashing attributes for the portal */ -+ uint32_t cpu; -+ uint32_t cache; -+ uint32_t window; -+ /* Specifies the stash request queue this portal should use */ -+ uint8_t sdest; -+ -+ /* Specifes a specific portal index to map or QBMAN_ANY_PORTAL_IDX -+ * for don't care. The portal index will be populated by the -+ * driver when the ioctl() successfully completes */ -+ uint32_t index; -+ -+ /* outputs */ -+ uint64_t cinh; -+ uint64_t cena; -+}; -+ -+#define USDPAA_IOCTL_ALLOC_RAW_PORTAL_COMPAT \ -+ _IOWR(USDPAA_IOCTL_MAGIC, 0x0C, struct compat_ioctl_raw_portal) -+ -+#define USDPAA_IOCTL_FREE_RAW_PORTAL_COMPAT \ -+ _IOR(USDPAA_IOCTL_MAGIC, 0x0D, struct compat_ioctl_raw_portal) -+ -+#endif -+ -+#ifdef __KERNEL__ -+ -+/* Early-boot hook */ -+int __init fsl_usdpaa_init_early(void); -+ -+/* Fault-handling in arch/powerpc/mm/mem.c gives USDPAA an opportunity to detect -+ * faults within its ranges via this hook. */ -+int usdpaa_test_fault(unsigned long pfn, u64 *phys_addr, u64 *size); -+ -+#endif /* __KERNEL__ */ -+ -+#endif /* CONFIG_FSL_USDPAA */ -+ -+#ifdef __KERNEL__ -+/* This interface is needed in a few places and though it's not specific to -+ * USDPAA as such, creating a new header for it doesn't make any sense. The -+ * qbman kernel driver implements this interface and uses it as the backend for -+ * both the FQID and BPID allocators. The fsl_usdpaa driver also uses this -+ * interface for tracking per-process allocations handed out to user-space. */ -+struct dpa_alloc { -+ struct list_head free; -+ spinlock_t lock; -+ struct list_head used; -+}; -+#define DECLARE_DPA_ALLOC(name) \ -+ struct dpa_alloc name = { \ -+ .free = { \ -+ .prev = &name.free, \ -+ .next = &name.free \ -+ }, \ -+ .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ -+ .used = { \ -+ .prev = &name.used, \ -+ .next = &name.used \ -+ } \ -+ } -+static inline void dpa_alloc_init(struct dpa_alloc *alloc) -+{ -+ INIT_LIST_HEAD(&alloc->free); -+ INIT_LIST_HEAD(&alloc->used); -+ spin_lock_init(&alloc->lock); -+} -+int dpa_alloc_new(struct dpa_alloc *alloc, u32 *result, u32 count, u32 align, -+ int partial); -+void dpa_alloc_free(struct dpa_alloc *alloc, u32 base_id, u32 count); -+void dpa_alloc_seed(struct dpa_alloc *alloc, u32 fqid, u32 count); -+ -+/* Like 'new' but specifies the desired range, returns -ENOMEM if the entire -+ * desired range is not available, or 0 for success. */ -+int dpa_alloc_reserve(struct dpa_alloc *alloc, u32 base_id, u32 count); -+/* Pops and returns contiguous ranges from the allocator. Returns -ENOMEM when -+ * 'alloc' is empty. */ -+int dpa_alloc_pop(struct dpa_alloc *alloc, u32 *result, u32 *count); -+/* Returns 1 if the specified id is alloced, 0 otherwise */ -+int dpa_alloc_check(struct dpa_alloc *list, u32 id); -+#endif /* __KERNEL__ */ -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif /* FSL_USDPAA_H */ |