diff options
Diffstat (limited to 'target/linux/layerscape/patches-4.4/7198-staging-fsl-mc-dpio-services-driver.patch')
-rw-r--r-- | target/linux/layerscape/patches-4.4/7198-staging-fsl-mc-dpio-services-driver.patch | 8943 |
1 files changed, 8943 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-4.4/7198-staging-fsl-mc-dpio-services-driver.patch b/target/linux/layerscape/patches-4.4/7198-staging-fsl-mc-dpio-services-driver.patch new file mode 100644 index 0000000000..7613d0a631 --- /dev/null +++ b/target/linux/layerscape/patches-4.4/7198-staging-fsl-mc-dpio-services-driver.patch @@ -0,0 +1,8943 @@ +From 331b26080961f0289c3a8a8e5e65f6524b23be19 Mon Sep 17 00:00:00 2001 +From: Jeffrey Ladouceur <Jeffrey.Ladouceur@freescale.com> +Date: Tue, 7 Apr 2015 23:24:55 -0400 +Subject: [PATCH 198/226] staging: fsl-mc: dpio services driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is a commit of a squash of the cummulative dpio services patches +in the sdk 2.0 kernel as of 3/7/2016. + +staging: fsl-mc: dpio: initial implementation of dpio services + +* Port from kernel 3.16 to 3.19 +* upgrade to match MC fw 7.0.0 +* return -EPROBE_DEFER if fsl_mc_portal_allocate() fails. +* enable DPIO interrupt support +* implement service FQDAN handling +* DPIO service selects DPIO objects using crude algorithms for now, we + will look to make this smarter later on. +* Locks all DPIO ops that aren't innately lockless. Smarter selection + logic may allow locking to be relaxed eventually. +* Portable QBMan driver source (and low-level MC flib code for DPIO) is + included and encapsulated within the DPIO driver. + +Signed-off-by: Geoff Thorpe <Geoff.Thorpe@freescale.com> +Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com> +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> +Signed-off-by: Ioana Radulescu <ruxandra.radulescu@freescale.com> +Signed-off-by: Cristian Sovaiala <cristian.sovaiala@freescale.com> +Signed-off-by: J. German Rivera <German.Rivera@freescale.com> +Signed-off-by: Jeffrey Ladouceur <Jeffrey.Ladouceur@freescale.com> +[Stuart: resolved merge conflicts] +Signed-off-by: Stuart Yoder <stuart.yoder@nxp.com> + +dpio: Use locks when querying fq state + +merged from patch in 3.19-bringup branch. + +Signed-off-by: Ioana Radulescu <ruxandra.radulescu@freescale.com> +Signed-off-by: Jeffrey Ladouceur <Jeffrey.Ladouceur@freescale.com> +Change-Id: Ia4d09f8a0cf4d8a4a2aa1cb39be789c34425286d +Reviewed-on: http://git.am.freescale.net:8181/34707 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Haiying Wang <Haiying.Wang@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +qbman: Fix potential race in VDQCR handling + +Remove atomic_read() check of the VDQCR busy marker. These checks were racy +as the flag could be incorrectly cleared if checked while another thread was +starting a pull command. The check is unneeded since we can determine the +owner of the outstanding pull command through other means. + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Change-Id: Icc64577c0a4ce6dadef208975e980adfc6796c86 +Reviewed-on: http://git.am.freescale.net:8181/34705 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Haiying Wang <Haiying.Wang@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +dpio: Fix IRQ handler and remove useless spinlock + +The IRQ handler for a threaded IRQ requires two parts: initally the handler +should check status and inhibit the IRQ then the threaded portion should +process and reenable. + +Also remove a spinlock that was redundant with the QMan driver and a debug +check that could trigger under a race condition + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Signed-off-by: Jeffrey Ladouceur <Jeffrey.Ladouceur@freescale.com> +Change-Id: I64926583af0be954228de94ae354fa005c8ec88a +Reviewed-on: http://git.am.freescale.net:8181/34706 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Haiying Wang <Haiying.Wang@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +staging: fsl-mc: dpio: Implement polling if IRQ not available + +Temporarly add a polling mode to DPIO in the case that the IRQ +registration fails + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Change-Id: Iebbd488fd14dd9878ef846e40f3ebcbcd0eb1e80 +Reviewed-on: http://git.am.freescale.net:8181/34775 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Jeffrey Ladouceur <Jeffrey.Ladouceur@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl-mc-dpio: Fix to make this work without interrupt + +Some additional fixes to make dpio driver work in poll mode. +This is needed for direct assignment to KVM Guest. + +Signed-off-by: Bharat Bhushan <Bharat.Bhushan@freescale.com> +Change-Id: Icf66b8c0c7f7e1610118f78396534c067f594934 +Reviewed-on: http://git.am.freescale.net:8181/35333 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl-mc-dpio: Make QBMan token tracking internal + +Previousy the QBMan portal code required the caller to properly set and +check for a token value used by the driver to detect when the QMan +hardware had completed a dequeue. This patch simplifes the driver +interface by internally dealing with token values. The driver will now +set the token value to 0 once it has dequeued a frame while a token +value of 1 indicates the HW has completed the dequeue but SW has not +consumed the frame yet. + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Change-Id: If94d9728b0faa0fd79b47108f5cb05a425b89c18 +Reviewed-on: http://git.am.freescale.net:8181/35433 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Haiying Wang <Haiying.Wang@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl-mc-dpio: Distribute DPIO IRQs among cores + +Configure the DPIO IRQ affinities across all available cores + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Change-Id: Ib45968a070460b7e9410bfe6067b20ecd3524c54 +Reviewed-on: http://git.am.freescale.net:8181/35540 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Haiying Wang <Haiying.Wang@freescale.com> +Reviewed-by: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +dpio/qbman: add flush after finishing cena write + +Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com> +Change-Id: I19537f101f7f5b443d60c0ad0e5d96c1dc302223 +Reviewed-on: http://git.am.freescale.net:8181/35854 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +dpio/qbman: rename qbman_dq_entry to qbman_result + +Currently qbman_dq_entry is used for both dq result in dqrr +and memory, and notifications in dqrr and memory. It doesn't +make sense to have dq_entry in name for those notifications +which have nothing to do with dq. So we rename this as +qbman_result which is meaningful for both cases. + +Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com> +Change-Id: I62b3e729c571a1195e8802a9fab3fca97a14eae4 +Reviewed-on: http://git.am.freescale.net:8181/35535 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +dpio/qbman: add APIs to parse BPSCN and CGCU + +BPSCN and CGCU are notifications which can only be written to memory. +We need to consider the host endianness while parsing these notification. +Also modify the check of FQRN/CSCN_MEM with the same consideration. + +Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com> +Change-Id: I572e0aa126107aed40e1ce326d5df7956882a939 +Reviewed-on: http://git.am.freescale.net:8181/35536 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +dpio/qbman: remove EXPORT_SYMBOL for qbman APIs + +because they are only used by dpio. + +Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com> +Change-Id: I12e7b81c2d32f3c7b3df9fd73b742b1b675f4b8b +Reviewed-on: http://git.am.freescale.net:8181/35537 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +dpio/qbman: add invalidate and prefetch support + +for cachable memory access. +Also remove the redundant memory barriers. + +Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com> +Change-Id: I452a768278d1c5ef37e5741e9b011d725cb57b30 +Reviewed-on: http://git.am.freescale.net:8181/35873 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +dpio-driver: Fix qman-portal interrupt masking in poll mode + +DPIO driver should mask qman-portal interrupt reporting When +working in poll mode. has_irq flag is used for same, but +interrupt maksing was happening before it was decided that +system will work in poll mode of interrupt mode. + +This patch fixes the issue and not irq masking/enabling is +happening after irq/poll mode is decided. + +Signed-off-by: Bharat Bhushan <Bharat.Bhushan@freescale.com> +Change-Id: I44de07b6142e80b3daea45e7d51a2d2799b2ed8d +Reviewed-on: http://git.am.freescale.net:8181/37100 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> +(cherry picked from commit 3579244250dcb287a0fe58bcc3b3780076d040a2) + +dpio: Add a function to query buffer pool depth + +Add a debug function thay allows users to query the number +of buffers in a specific buffer pool + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Change-Id: Ie9a5f2e86d6a04ae61868bcc807121780c53cf6c +Reviewed-on: http://git.am.freescale.net:8181/36069 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> +(cherry picked from commit 3c749d860592f62f6b219232580ca35fd1075337) + +dpio: Use normal cachable non-shareable memory for qbman cena + +QBMan SWP CENA portal memory requires the memory to be cacheable, +and non-shareable. + +Signed-off-by: Haiying Wang <Haiying.Wang@freescale.com> +Change-Id: I1c01cffe9ff2503fea2396d7cc761508f6e1ca85 +Reviewed-on: http://git.am.freescale.net:8181/35487 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> +(cherry picked from commit 2a7e1ede7e155d9219006999893912e0b029ce4c) + +fsl-dpio: Process frames in IRQ context + +Stop using threaded IRQs and move back to hardirq top-halves. +This is the first patch of a small series adapting the DPIO and Ethernet +code to these changes. + +Signed-off-by: Roy Pledge <roy.pledge@freescale.com> +Tested-by: Ioana Radulescu <ruxandra.radulescu@freescale.com> +Tested-by: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> +[Stuart: split out dpaa-eth part separately] +Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl-dpio: Fast DPIO object selection + +The DPIO service code had a couple of problems with performance impact: + - The DPIO service object was protected by a global lock, within + functions called from the fast datapath on multiple CPUs. + - The DPIO service code would iterate unnecessarily through its linked + list, while most of the time it looks for CPU-bound objects. + +Add a fast-access array pointing to the same dpaa_io objects as the DPIO +service's linked list, used in non-preemptible contexts. +Avoid list access/reordering if a specific CPU was requested. This +greatly limits contention on the global service lock. +Make explicit calls for per-CPU DPIO service objects if the current +context permits (which is the case on most of the Ethernet fastpath). + +These changes incidentally fix a functional problem, too: according to +the specification of struct dpaa_io_notification_ctx, registration should +fail if the specification of 'desired_cpu' cannot be observed. Instead, +dpaa_io_service_register() would keep searching for non-affine DPIO +objects, even when that was not requested. + +Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> +Change-Id: I2dd78bc56179f97d3fd78052a653456e5f89ed82 +Reviewed-on: http://git.am.freescale.net:8181/37689 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Roy Pledge <roy.pledge@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +DPIO: Implement a missing lock in DPIO + +Implement missing DPIO service notification deregistration lock + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Change-Id: Ida9a4d00cc3a66bc215c260a8df2b197366736f7 +Reviewed-on: http://git.am.freescale.net:8181/38497 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Haiying Wang <Haiying.Wang@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +staging: fsl-mc: migrated dpio flibs for MC fw 8.0.0 + +Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl_qbman: Ensure SDQCR is only enabled if a channel is selected + +QMan HW considers an SDQCR command that does not indicate any +channels to dequeue from to be an error. This change ensures that +a NULL command is set in the case no channels are selected for dequeue + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +Change-Id: I8861304881885db00df4a29d760848990d706c70 +Reviewed-on: http://git.am.freescale.net:8181/38498 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Haiying Wang <Haiying.Wang@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +flib: dpio: Fix compiler warning. + +Gcc takes the credit here. +To be merged with other fixes on this branch. + +Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> +Change-Id: If81f35ab3e8061aae1e03b72ab16a4c1dc390c3a +Reviewed-on: http://git.am.freescale.net:8181/39148 +Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> +Reviewed-by: Stuart Yoder <stuart.yoder@freescale.com> + +staging: fsl-mc: dpio: remove programing of MSIs in dpio driver + +this is now handled in the bus driver + +Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl_qbman: Enable CDAN generation + +Enable CDAN notificiation registration in both QBMan and DPIO + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> + +fsl_dpio: Implement API to dequeue from a channel + +Implement an API that allows users to dequeue from a channel + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> + +fsl-dpio: Change dequeue command type + +For now CDANs don't work with priority precedence. + +Signed-off-by: Ioana Radulescu <ruxandra.radulescu@freescale.com> + +fsl-dpio: Export FQD context getter function + +Signed-off-by: Ioana Radulescu <ruxandra.radulescu@freescale.com> + +fsl_dpio: Fix DPIO polling thread logic + +Fix the logic for the DPIO polling logic and ensure the thread +is not parked + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> +[Stuart: fixed typo in comment] +Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl-dpio,qbman: Export functions + +A few of the functions used by the Ethernet driver were not exported +yet. Needed in order to compile Eth driver as a module. + +Signed-off-by: Ioana Radulescu <ruxandra.radulescu@freescale.com> +Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl_qbman: Use proper accessors when reading QBMan portals + +Use accessors that properly byteswap when accessing QBMan portals + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> + +fsl_qbman: Fix encoding of 64 byte values + +The QBMan driver encodes commands in 32 bit host endianess then +coverts to little endian before sending to HW. This means 64 +byte values need to be encoded so that the values will be +correctly swapped when the commands are written to HW. + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> + +dpaa_fd: Add functions for SG entries endianness conversions + +Scatter gather entries are little endian at the hardware level. +Add functions for converting the SG entry structure to cpu +endianness to avoid incorrect behaviour on BE kernels. + +Signed-off-by: Ioana Radulescu <ruxandra.radulescu@freescale.com> + +fsl_dpaa: update header files with kernel-doc format + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> + +qbman: update header fiels to follow kernel-doc format + +Plus rename orp_id as opr_id based on the BG. + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> + +fsl/dpio: rename ldpaa to dpaa2 + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> +(Stuart: removed eth part out into separate patch) +Signed-off-by: Stuart Yoder <stuart.yoder@nxp.com> + +qbman_test: update qbman_test + +- Update to sync with latest change in qbman driver. +- Add bpscn test case + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> + +fsl-dpio: add FLE (Frame List Entry) for FMT=dpaa_fd_list support + +Signed-off-by: Horia Geantă <horia.geanta@freescale.com> + +fsl-dpio: add accessors for FD[FRC] + +Signed-off-by: Horia Geantă <horia.geanta@freescale.com> + +fsl-dpio: add accessors for FD[FLC] + +Signed-off-by: Horia Geantă <horia.geanta@freescale.com> +(Stuart: corrected typo in subject) +Signed-off-by: Stuart Yoder <stuart.yoder@nxp.com> + +fsl/dpio: dpaa2_fd: Add the comments for newly added APIs. + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> +[Stuart: added fsl/dpio prefix on commit subject] +Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com> + +fsl-dpio: rename dpaa_* structure to dpaa2_* + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> +(Stuart: split eth and caam parts out into separate patches) +Signed-off-by: Stuart Yoder <stuart.yoder@nxp.com> + +fsl-dpio: update the header file with more description in comments + +plus fix some typos. + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> + +fsl-dpio: fix Klocwork issues. + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> + +fsl_dpio: Fix kernel doc issues and add an overview + +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> + +fsl-dpio,qbman: Prefer affine portal to acquire/release buffers + +The FQ enqueue/dequeue DPIO code attempts to select an affine QBMan +portal in order to minimize contention (under the assumption that most +of the calling code runs in affine contexts). Doing the same now for +buffer acquire/release. + +Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> + +fsl-dpio: prefer affine QBMan portal in dpaa2_io_service_enqueue_fq + +Commit 7b057d9bc3d31 ("fsl-dpio: Fast DPIO object selection") +took care of dpaa2_io_service_enqueue_qd, missing +dpaa2_io_service_enqueue_fq. + +Cc: Bogdan Hamciuc <bogdan.hamciuc@freescale.com> +Signed-off-by: Horia Geantă <horia.geanta@freescale.com> + +fsl/dpio: update the dpio flib files from mc9.0.0 release + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> + +fsl/dpio: pass qman_version from dpio attributes to swp desc + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> + +fsl/dpio/qbman: Use qman version to determin dqrr size + +Signed-off-by: Haiying Wang <Haiying.wang@freescale.com> + +fsl-dpio: Fix dequeue type enum values + +enum qbman_pull_type_e did not follow the volatile dequeue command +specification, for which VERB=b'00 is a valid value (but of no +interest to us). + +Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> + +fsl-dpio: Volatile dequeue with priority precedence + +Use priority precedence to do volatile dequeue from channels, rather +than active FQ precedence. + +Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> +Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> + +Signed-off-by: Stuart Yoder <stuart.yoder@nxp.com> +--- + drivers/staging/fsl-mc/bus/Kconfig | 16 + + drivers/staging/fsl-mc/bus/Makefile | 3 + + drivers/staging/fsl-mc/bus/dpio/Makefile | 9 + + drivers/staging/fsl-mc/bus/dpio/dpio-drv.c | 405 +++++++ + drivers/staging/fsl-mc/bus/dpio/dpio-drv.h | 33 + + drivers/staging/fsl-mc/bus/dpio/dpio.c | 468 ++++++++ + drivers/staging/fsl-mc/bus/dpio/dpio_service.c | 801 +++++++++++++ + drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h | 460 ++++++++ + drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h | 184 +++ + drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h | 123 ++ + drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h | 753 ++++++++++++ + drivers/staging/fsl-mc/bus/dpio/qbman_debug.c | 846 ++++++++++++++ + drivers/staging/fsl-mc/bus/dpio/qbman_debug.h | 136 +++ + drivers/staging/fsl-mc/bus/dpio/qbman_portal.c | 1212 ++++++++++++++++++++ + drivers/staging/fsl-mc/bus/dpio/qbman_portal.h | 261 +++++ + drivers/staging/fsl-mc/bus/dpio/qbman_private.h | 173 +++ + drivers/staging/fsl-mc/bus/dpio/qbman_sys.h | 307 +++++ + drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h | 86 ++ + drivers/staging/fsl-mc/bus/dpio/qbman_test.c | 664 +++++++++++ + drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h | 774 +++++++++++++ + drivers/staging/fsl-mc/include/fsl_dpaa2_io.h | 619 ++++++++++ + 21 files changed, 8333 insertions(+) + create mode 100644 drivers/staging/fsl-mc/bus/dpio/Makefile + create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio-drv.c + create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio-drv.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio.c + create mode 100644 drivers/staging/fsl-mc/bus/dpio/dpio_service.c + create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_debug.c + create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_debug.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_portal.c + create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_portal.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_private.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_sys.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h + create mode 100644 drivers/staging/fsl-mc/bus/dpio/qbman_test.c + create mode 100644 drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h + create mode 100644 drivers/staging/fsl-mc/include/fsl_dpaa2_io.h + +--- a/drivers/staging/fsl-mc/bus/Kconfig ++++ b/drivers/staging/fsl-mc/bus/Kconfig +@@ -28,3 +28,19 @@ config FSL_MC_RESTOOL + help + Driver that provides kernel support for the Freescale Management + Complex resource manager user-space tool. ++ ++config FSL_MC_DPIO ++ tristate "Freescale Data Path I/O (DPIO) driver" ++ depends on FSL_MC_BUS ++ help ++ Driver for Freescale Data Path I/O (DPIO) devices. ++ A DPIO device provides queue and buffer management facilities ++ for software to interact with other Data Path devices. This ++ driver does not expose the DPIO device individually, but ++ groups them under a service layer API. ++ ++config FSL_QBMAN_DEBUG ++ tristate "Freescale QBMAN Debug APIs" ++ depends on FSL_MC_DPIO ++ help ++ QBMan debug assistant APIs. +--- a/drivers/staging/fsl-mc/bus/Makefile ++++ b/drivers/staging/fsl-mc/bus/Makefile +@@ -21,3 +21,6 @@ mc-bus-driver-objs := mc-bus.o \ + + # MC restool kernel support + obj-$(CONFIG_FSL_MC_RESTOOL) += mc-restool.o ++ ++# MC DPIO driver ++obj-$(CONFIG_FSL_MC_DPIO) += dpio/ +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/Makefile +@@ -0,0 +1,9 @@ ++# ++# Freescale DPIO driver ++# ++ ++obj-$(CONFIG_FSL_MC_BUS) += fsl-dpio-drv.o ++ ++fsl-dpio-drv-objs := dpio-drv.o dpio_service.o dpio.o qbman_portal.o ++ ++obj-$(CONFIG_FSL_QBMAN_DEBUG) += qbman_debug.o +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.c +@@ -0,0 +1,405 @@ ++/* 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. ++ */ ++ ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/interrupt.h> ++#include <linux/msi.h> ++#include <linux/dma-mapping.h> ++#include <linux/kthread.h> ++#include <linux/delay.h> ++ ++#include "../../include/mc.h" ++#include "../../include/fsl_dpaa2_io.h" ++ ++#include "fsl_qbman_portal.h" ++#include "fsl_dpio.h" ++#include "fsl_dpio_cmd.h" ++ ++#include "dpio-drv.h" ++ ++#define DPIO_DESCRIPTION "DPIO Driver" ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_AUTHOR("Freescale Semiconductor, Inc"); ++MODULE_DESCRIPTION(DPIO_DESCRIPTION); ++ ++#define MAX_DPIO_IRQ_NAME 16 /* Big enough for "FSL DPIO %d" */ ++ ++struct dpio_priv { ++ struct dpaa2_io *io; ++ char irq_name[MAX_DPIO_IRQ_NAME]; ++ struct task_struct *thread; ++}; ++ ++static int dpio_thread(void *data) ++{ ++ struct dpaa2_io *io = data; ++ ++ while (!kthread_should_stop()) { ++ int err = dpaa2_io_poll(io); ++ ++ if (err) { ++ pr_err("dpaa2_io_poll() failed\n"); ++ return err; ++ } ++ msleep(50); ++ } ++ return 0; ++} ++ ++static irqreturn_t dpio_irq_handler(int irq_num, void *arg) ++{ ++ struct device *dev = (struct device *)arg; ++ struct dpio_priv *priv = dev_get_drvdata(dev); ++ ++ return dpaa2_io_irq(priv->io); ++} ++ ++static void unregister_dpio_irq_handlers(struct fsl_mc_device *ls_dev) ++{ ++ int i; ++ struct fsl_mc_device_irq *irq; ++ int irq_count = ls_dev->obj_desc.irq_count; ++ ++ for (i = 0; i < irq_count; i++) { ++ irq = ls_dev->irqs[i]; ++ devm_free_irq(&ls_dev->dev, irq->msi_desc->irq, &ls_dev->dev); ++ } ++} ++ ++static int register_dpio_irq_handlers(struct fsl_mc_device *ls_dev, int cpu) ++{ ++ struct dpio_priv *priv; ++ unsigned int i; ++ int error; ++ struct fsl_mc_device_irq *irq; ++ unsigned int num_irq_handlers_registered = 0; ++ int irq_count = ls_dev->obj_desc.irq_count; ++ cpumask_t mask; ++ ++ priv = dev_get_drvdata(&ls_dev->dev); ++ ++ if (WARN_ON(irq_count != 1)) ++ return -EINVAL; ++ ++ for (i = 0; i < irq_count; i++) { ++ irq = ls_dev->irqs[i]; ++ error = devm_request_irq(&ls_dev->dev, ++ irq->msi_desc->irq, ++ dpio_irq_handler, ++ 0, ++ priv->irq_name, ++ &ls_dev->dev); ++ if (error < 0) { ++ dev_err(&ls_dev->dev, ++ "devm_request_irq() failed: %d\n", ++ error); ++ goto error_unregister_irq_handlers; ++ } ++ ++ /* Set the IRQ affinity */ ++ cpumask_clear(&mask); ++ cpumask_set_cpu(cpu, &mask); ++ if (irq_set_affinity(irq->msi_desc->irq, &mask)) ++ pr_err("irq_set_affinity failed irq %d cpu %d\n", ++ irq->msi_desc->irq, cpu); ++ ++ num_irq_handlers_registered++; ++ } ++ ++ return 0; ++ ++error_unregister_irq_handlers: ++ for (i = 0; i < num_irq_handlers_registered; i++) { ++ irq = ls_dev->irqs[i]; ++ devm_free_irq(&ls_dev->dev, irq->msi_desc->irq, ++ &ls_dev->dev); ++ } ++ ++ return error; ++} ++ ++static int __cold ++dpaa2_dpio_probe(struct fsl_mc_device *ls_dev) ++{ ++ struct dpio_attr dpio_attrs; ++ struct dpaa2_io_desc desc; ++ struct dpio_priv *priv; ++ int err = -ENOMEM; ++ struct device *dev = &ls_dev->dev; ++ struct dpaa2_io *defservice; ++ bool irq_allocated = false; ++ static int next_cpu; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ goto err_priv_alloc; ++ ++ dev_set_drvdata(dev, priv); ++ ++ err = fsl_mc_portal_allocate(ls_dev, 0, &ls_dev->mc_io); ++ if (err) { ++ dev_err(dev, "MC portal allocation failed\n"); ++ err = -EPROBE_DEFER; ++ goto err_mcportal; ++ } ++ ++ err = dpio_open(ls_dev->mc_io, 0, ls_dev->obj_desc.id, ++ &ls_dev->mc_handle); ++ if (err) { ++ dev_err(dev, "dpio_open() failed\n"); ++ goto err_open; ++ } ++ ++ err = dpio_get_attributes(ls_dev->mc_io, 0, ls_dev->mc_handle, ++ &dpio_attrs); ++ if (err) { ++ dev_err(dev, "dpio_get_attributes() failed %d\n", err); ++ goto err_get_attr; ++ } ++ err = dpio_enable(ls_dev->mc_io, 0, ls_dev->mc_handle); ++ if (err) { ++ dev_err(dev, "dpio_enable() failed %d\n", err); ++ goto err_get_attr; ++ } ++ pr_info("ce_paddr=0x%llx, ci_paddr=0x%llx, portalid=%d, prios=%d\n", ++ ls_dev->regions[0].start, ++ ls_dev->regions[1].start, ++ dpio_attrs.qbman_portal_id, ++ dpio_attrs.num_priorities); ++ ++ pr_info("ce_size=0x%llx, ci_size=0x%llx\n", ++ resource_size(&ls_dev->regions[0]), ++ resource_size(&ls_dev->regions[1])); ++ ++ desc.qman_version = dpio_attrs.qbman_version; ++ /* Build DPIO driver object out of raw MC object */ ++ desc.receives_notifications = dpio_attrs.num_priorities ? 1 : 0; ++ desc.has_irq = 1; ++ desc.will_poll = 1; ++ desc.has_8prio = dpio_attrs.num_priorities == 8 ? 1 : 0; ++ desc.cpu = next_cpu; ++ desc.stash_affinity = 1; /* TODO: Figure out how to determine ++ this setting - will we ever have non-affine ++ portals where we stash to a platform cache? */ ++ next_cpu = (next_cpu + 1) % num_active_cpus(); ++ desc.dpio_id = ls_dev->obj_desc.id; ++ desc.regs_cena = ioremap_cache_ns(ls_dev->regions[0].start, ++ resource_size(&ls_dev->regions[0])); ++ desc.regs_cinh = ioremap(ls_dev->regions[1].start, ++ resource_size(&ls_dev->regions[1])); ++ ++ err = fsl_mc_allocate_irqs(ls_dev); ++ if (err) { ++ dev_err(dev, "DPIO fsl_mc_allocate_irqs failed\n"); ++ desc.has_irq = 0; ++ } else { ++ irq_allocated = true; ++ ++ snprintf(priv->irq_name, MAX_DPIO_IRQ_NAME, "FSL DPIO %d", ++ desc.dpio_id); ++ ++ err = register_dpio_irq_handlers(ls_dev, desc.cpu); ++ if (err) ++ desc.has_irq = 0; ++ } ++ ++ priv->io = dpaa2_io_create(&desc); ++ if (!priv->io) { ++ dev_err(dev, "DPIO setup failed\n"); ++ goto err_dpaa2_io_create; ++ } ++ ++ /* If no irq then go to poll mode */ ++ if (desc.has_irq == 0) { ++ dev_info(dev, "Using polling mode for DPIO %d\n", ++ desc.dpio_id); ++ /* goto err_register_dpio_irq; */ ++ /* TEMP: Start polling if IRQ could not ++ be registered. This will go away once ++ KVM support for MSI is present */ ++ if (irq_allocated == true) ++ fsl_mc_free_irqs(ls_dev); ++ ++ if (desc.stash_affinity) ++ priv->thread = kthread_create_on_cpu(dpio_thread, ++ priv->io, ++ desc.cpu, ++ "dpio_aff%u"); ++ else ++ priv->thread = ++ kthread_create(dpio_thread, ++ priv->io, ++ "dpio_non%u", ++ dpio_attrs.qbman_portal_id); ++ if (IS_ERR(priv->thread)) { ++ dev_err(dev, "DPIO thread failure\n"); ++ err = PTR_ERR(priv->thread); ++ goto err_dpaa_thread; ++ } ++ kthread_unpark(priv->thread); ++ wake_up_process(priv->thread); ++ } ++ ++ defservice = dpaa2_io_default_service(); ++ err = dpaa2_io_service_add(defservice, priv->io); ++ dpaa2_io_down(defservice); ++ if (err) { ++ dev_err(dev, "DPIO add-to-service failed\n"); ++ goto err_dpaa2_io_add; ++ } ++ ++ dev_info(dev, "dpio: probed object %d\n", ls_dev->obj_desc.id); ++ dev_info(dev, " receives_notifications = %d\n", ++ desc.receives_notifications); ++ dev_info(dev, " has_irq = %d\n", desc.has_irq); ++ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); ++ fsl_mc_portal_free(ls_dev->mc_io); ++ return 0; ++ ++err_dpaa2_io_add: ++ unregister_dpio_irq_handlers(ls_dev); ++/* TEMP: To be restored once polling is removed ++ err_register_dpio_irq: ++ fsl_mc_free_irqs(ls_dev); ++*/ ++err_dpaa_thread: ++err_dpaa2_io_create: ++ dpio_disable(ls_dev->mc_io, 0, ls_dev->mc_handle); ++err_get_attr: ++ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); ++err_open: ++ fsl_mc_portal_free(ls_dev->mc_io); ++err_mcportal: ++ dev_set_drvdata(dev, NULL); ++ devm_kfree(dev, priv); ++err_priv_alloc: ++ return err; ++} ++ ++/* ++ * Tear down interrupts for a given DPIO object ++ */ ++static void dpio_teardown_irqs(struct fsl_mc_device *ls_dev) ++{ ++ /* (void)disable_dpio_irqs(ls_dev); */ ++ unregister_dpio_irq_handlers(ls_dev); ++ fsl_mc_free_irqs(ls_dev); ++} ++ ++static int __cold ++dpaa2_dpio_remove(struct fsl_mc_device *ls_dev) ++{ ++ struct device *dev; ++ struct dpio_priv *priv; ++ int err; ++ ++ dev = &ls_dev->dev; ++ priv = dev_get_drvdata(dev); ++ ++ /* there is no implementation yet for pulling a DPIO object out of a ++ * running service (and they're currently always running). ++ */ ++ dev_crit(dev, "DPIO unplugging is broken, the service holds onto it\n"); ++ ++ if (priv->thread) ++ kthread_stop(priv->thread); ++ else ++ dpio_teardown_irqs(ls_dev); ++ ++ err = fsl_mc_portal_allocate(ls_dev, 0, &ls_dev->mc_io); ++ if (err) { ++ dev_err(dev, "MC portal allocation failed\n"); ++ goto err_mcportal; ++ } ++ ++ err = dpio_open(ls_dev->mc_io, 0, ls_dev->obj_desc.id, ++ &ls_dev->mc_handle); ++ if (err) { ++ dev_err(dev, "dpio_open() failed\n"); ++ goto err_open; ++ } ++ ++ dev_set_drvdata(dev, NULL); ++ dpaa2_io_down(priv->io); ++ ++ err = 0; ++ ++ dpio_disable(ls_dev->mc_io, 0, ls_dev->mc_handle); ++ dpio_close(ls_dev->mc_io, 0, ls_dev->mc_handle); ++err_open: ++ fsl_mc_portal_free(ls_dev->mc_io); ++err_mcportal: ++ return err; ++} ++ ++static const struct fsl_mc_device_match_id dpaa2_dpio_match_id_table[] = { ++ { ++ .vendor = FSL_MC_VENDOR_FREESCALE, ++ .obj_type = "dpio", ++ .ver_major = DPIO_VER_MAJOR, ++ .ver_minor = DPIO_VER_MINOR ++ }, ++ { .vendor = 0x0 } ++}; ++ ++static struct fsl_mc_driver dpaa2_dpio_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = dpaa2_dpio_probe, ++ .remove = dpaa2_dpio_remove, ++ .match_id_table = dpaa2_dpio_match_id_table ++}; ++ ++static int dpio_driver_init(void) ++{ ++ int err; ++ ++ err = dpaa2_io_service_driver_init(); ++ if (!err) { ++ err = fsl_mc_driver_register(&dpaa2_dpio_driver); ++ if (err) ++ dpaa2_io_service_driver_exit(); ++ } ++ return err; ++} ++static void dpio_driver_exit(void) ++{ ++ fsl_mc_driver_unregister(&dpaa2_dpio_driver); ++ dpaa2_io_service_driver_exit(); ++} ++module_init(dpio_driver_init); ++module_exit(dpio_driver_exit); +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/dpio-drv.h +@@ -0,0 +1,33 @@ ++/* 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. ++ */ ++ ++int dpaa2_io_service_driver_init(void); ++void dpaa2_io_service_driver_exit(void); +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/dpio.c +@@ -0,0 +1,468 @@ ++/* Copyright 2013-2015 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 the above-listed copyright holders nor the ++ * names of any 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDERS OR CONTRIBUTORS 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 "../../include/mc-sys.h" ++#include "../../include/mc-cmd.h" ++#include "fsl_dpio.h" ++#include "fsl_dpio_cmd.h" ++ ++int dpio_open(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ int dpio_id, ++ uint16_t *token) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_OPEN, ++ cmd_flags, ++ 0); ++ DPIO_CMD_OPEN(cmd, dpio_id); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); ++ ++ return 0; ++} ++ ++int dpio_close(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLOSE, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_create(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ const struct dpio_cfg *cfg, ++ uint16_t *token) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CREATE, ++ cmd_flags, ++ 0); ++ DPIO_CMD_CREATE(cmd, cfg); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ *token = MC_CMD_HDR_READ_TOKEN(cmd.header); ++ ++ return 0; ++} ++ ++int dpio_destroy(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_DESTROY, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_enable(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_ENABLE, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_disable(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_DISABLE, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_is_enabled(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ int *en) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_IS_ENABLED, cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ DPIO_RSP_IS_ENABLED(cmd, *en); ++ ++ return 0; ++} ++ ++int dpio_reset(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_RESET, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_set_irq(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ struct dpio_irq_cfg *irq_cfg) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ, ++ cmd_flags, ++ token); ++ DPIO_CMD_SET_IRQ(cmd, irq_index, irq_cfg); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_get_irq(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ int *type, ++ struct dpio_irq_cfg *irq_cfg) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ, ++ cmd_flags, ++ token); ++ DPIO_CMD_GET_IRQ(cmd, irq_index); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ DPIO_RSP_GET_IRQ(cmd, *type, irq_cfg); ++ ++ return 0; ++} ++ ++int dpio_set_irq_enable(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint8_t en) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ_ENABLE, ++ cmd_flags, ++ token); ++ DPIO_CMD_SET_IRQ_ENABLE(cmd, irq_index, en); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_get_irq_enable(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint8_t *en) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_ENABLE, ++ cmd_flags, ++ token); ++ DPIO_CMD_GET_IRQ_ENABLE(cmd, irq_index); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ DPIO_RSP_GET_IRQ_ENABLE(cmd, *en); ++ ++ return 0; ++} ++ ++int dpio_set_irq_mask(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint32_t mask) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_IRQ_MASK, ++ cmd_flags, ++ token); ++ DPIO_CMD_SET_IRQ_MASK(cmd, irq_index, mask); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_get_irq_mask(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint32_t *mask) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_MASK, ++ cmd_flags, ++ token); ++ DPIO_CMD_GET_IRQ_MASK(cmd, irq_index); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ DPIO_RSP_GET_IRQ_MASK(cmd, *mask); ++ ++ return 0; ++} ++ ++int dpio_get_irq_status(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint32_t *status) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_IRQ_STATUS, ++ cmd_flags, ++ token); ++ DPIO_CMD_GET_IRQ_STATUS(cmd, irq_index, *status); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ DPIO_RSP_GET_IRQ_STATUS(cmd, *status); ++ ++ return 0; ++} ++ ++int dpio_clear_irq_status(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint32_t status) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLEAR_IRQ_STATUS, ++ cmd_flags, ++ token); ++ DPIO_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_get_attributes(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ struct dpio_attr *attr) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_ATTR, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ DPIO_RSP_GET_ATTR(cmd, attr); ++ ++ return 0; ++} ++ ++int dpio_set_stashing_destination(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t sdest) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_STASHING_DEST, ++ cmd_flags, ++ token); ++ DPIO_CMD_SET_STASHING_DEST(cmd, sdest); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++int dpio_get_stashing_destination(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t *sdest) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_STASHING_DEST, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ DPIO_RSP_GET_STASHING_DEST(cmd, *sdest); ++ ++ return 0; ++} ++ ++int dpio_add_static_dequeue_channel(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ int dpcon_id, ++ uint8_t *channel_index) ++{ ++ struct mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPIO_CMDID_ADD_STATIC_DEQUEUE_CHANNEL, ++ cmd_flags, ++ token); ++ DPIO_CMD_ADD_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ DPIO_RSP_ADD_STATIC_DEQUEUE_CHANNEL(cmd, *channel_index); ++ ++ return 0; ++} ++ ++int dpio_remove_static_dequeue_channel(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ int dpcon_id) ++{ ++ struct mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header( ++ DPIO_CMDID_REMOVE_STATIC_DEQUEUE_CHANNEL, ++ cmd_flags, ++ token); ++ DPIO_CMD_REMOVE_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/dpio_service.c +@@ -0,0 +1,801 @@ ++/* 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. ++ */ ++#include <linux/types.h> ++#include "fsl_qbman_portal.h" ++#include "../../include/mc.h" ++#include "../../include/fsl_dpaa2_io.h" ++#include "fsl_dpio.h" ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/interrupt.h> ++#include <linux/dma-mapping.h> ++#include <linux/slab.h> ++ ++#include "dpio-drv.h" ++#include "qbman_debug.h" ++ ++#define UNIMPLEMENTED() pr_err("FOO: %s unimplemented!\n", __func__) ++ ++#define MAGIC_SERVICE 0xabcd9876 ++#define MAGIC_OBJECT 0x1234fedc ++ ++struct dpaa2_io { ++ /* If MAGIC_SERVICE, this is a group of objects, use the 'service' part ++ * of the union. If MAGIC_OBJECT, use the 'object' part of the union. If ++ * it's neither, something got corrupted. This is mainly to satisfy ++ * dpaa2_io_from_registration(), which dereferences a caller- ++ * instantiated struct and so warrants a bug-checking step - hence the ++ * magic rather than a boolean. ++ */ ++ unsigned int magic; ++ atomic_t refs; ++ union { ++ struct dpaa2_io_service { ++ spinlock_t lock; ++ struct list_head list; ++ /* for targeted dpaa2_io selection */ ++ struct dpaa2_io *objects_by_cpu[NR_CPUS]; ++ cpumask_t cpus_notifications; ++ cpumask_t cpus_stashing; ++ int has_nonaffine; ++ /* slight hack. record the special case of the ++ * "default service", because that's the case where we ++ * need to avoid a kfree() ... */ ++ int is_defservice; ++ } service; ++ struct dpaa2_io_object { ++ struct dpaa2_io_desc dpio_desc; ++ struct qbman_swp_desc swp_desc; ++ struct qbman_swp *swp; ++ /* If the object is part of a service, this is it (and ++ * 'node' is linked into the service's list) */ ++ struct dpaa2_io *service; ++ struct list_head node; ++ /* Interrupt mask, as used with ++ * qbman_swp_interrupt_[gs]et_vanish(). This isn't ++ * locked, because the higher layer is driving all ++ * "ingress" processing. */ ++ uint32_t irq_mask; ++ /* As part of simplifying assumptions, we provide an ++ * irq-safe lock for each type of DPIO operation that ++ * isn't innately lockless. The selection algorithms ++ * (which are simplified) require this, whereas ++ * eventually adherence to cpu-affinity will presumably ++ * relax the locking requirements. */ ++ spinlock_t lock_mgmt_cmd; ++ spinlock_t lock_notifications; ++ struct list_head notifications; ++ } object; ++ }; ++}; ++ ++struct dpaa2_io_store { ++ unsigned int max; ++ dma_addr_t paddr; ++ struct dpaa2_dq *vaddr; ++ void *alloced_addr; /* the actual return from kmalloc as it may ++ be adjusted for alignment purposes */ ++ unsigned int idx; /* position of the next-to-be-returned entry */ ++ struct qbman_swp *swp; /* portal used to issue VDQCR */ ++ struct device *dev; /* device used for DMA mapping */ ++}; ++ ++static struct dpaa2_io def_serv; ++ ++/**********************/ ++/* Internal functions */ ++/**********************/ ++ ++static void service_init(struct dpaa2_io *d, int is_defservice) ++{ ++ struct dpaa2_io_service *s = &d->service; ++ ++ d->magic = MAGIC_SERVICE; ++ atomic_set(&d->refs, 1); ++ spin_lock_init(&s->lock); ++ INIT_LIST_HEAD(&s->list); ++ cpumask_clear(&s->cpus_notifications); ++ cpumask_clear(&s->cpus_stashing); ++ s->has_nonaffine = 0; ++ s->is_defservice = is_defservice; ++} ++ ++/* Selection algorithms, stupid ones at that. These are to handle the case where ++ * the given dpaa2_io is a service, by choosing the non-service dpaa2_io within ++ * it to use. ++ */ ++static struct dpaa2_io *_service_select_by_cpu_slow(struct dpaa2_io_service *ss, ++ int cpu) ++{ ++ struct dpaa2_io *o; ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&ss->lock, irqflags); ++ /* TODO: this is about the dumbest and slowest selection algorithm you ++ * could imagine. (We're looking for something working first, and ++ * something efficient second...) ++ */ ++ list_for_each_entry(o, &ss->list, object.node) ++ if (o->object.dpio_desc.cpu == cpu) ++ goto found; ++ ++ /* No joy. Try the first nonaffine portal (bleurgh) */ ++ if (ss->has_nonaffine) ++ list_for_each_entry(o, &ss->list, object.node) ++ if (!o->object.dpio_desc.stash_affinity) ++ goto found; ++ ++ /* No joy. Try the first object. Told you it was horrible. */ ++ if (!list_empty(&ss->list)) ++ o = list_entry(ss->list.next, struct dpaa2_io, object.node); ++ else ++ o = NULL; ++ ++found: ++ spin_unlock_irqrestore(&ss->lock, irqflags); ++ return o; ++} ++ ++static struct dpaa2_io *service_select_by_cpu(struct dpaa2_io *d, int cpu) ++{ ++ struct dpaa2_io_service *ss; ++ unsigned long irqflags; ++ ++ if (!d) ++ d = &def_serv; ++ else if (d->magic == MAGIC_OBJECT) ++ return d; ++ BUG_ON(d->magic != MAGIC_SERVICE); ++ ++ ss = &d->service; ++ ++ /* If cpu==-1, choose the current cpu, with no guarantees about ++ * potentially being migrated away. ++ */ ++ if (unlikely(cpu < 0)) { ++ spin_lock_irqsave(&ss->lock, irqflags); ++ cpu = smp_processor_id(); ++ spin_unlock_irqrestore(&ss->lock, irqflags); ++ ++ return _service_select_by_cpu_slow(ss, cpu); ++ } ++ ++ /* If a specific cpu was requested, pick it up immediately */ ++ return ss->objects_by_cpu[cpu]; ++} ++ ++static inline struct dpaa2_io *service_select_any(struct dpaa2_io *d) ++{ ++ struct dpaa2_io_service *ss; ++ struct dpaa2_io *o; ++ unsigned long irqflags; ++ ++ if (!d) ++ d = &def_serv; ++ else if (d->magic == MAGIC_OBJECT) ++ return d; ++ BUG_ON(d->magic != MAGIC_SERVICE); ++ ++ /* ++ * Lock the service, looking for the first DPIO object in the list, ++ * ignore everything else about that DPIO, and choose it to do the ++ * operation! As a post-selection step, move the DPIO to the end of ++ * the list. It should improve load-balancing a little, although it ++ * might also incur a performance hit, given that the lock is *global* ++ * and this may be called on the fast-path... ++ */ ++ ss = &d->service; ++ spin_lock_irqsave(&ss->lock, irqflags); ++ if (!list_empty(&ss->list)) { ++ o = list_entry(ss->list.next, struct dpaa2_io, object.node); ++ list_del(&o->object.node); ++ list_add_tail(&o->object.node, &ss->list); ++ } else ++ o = NULL; ++ spin_unlock_irqrestore(&ss->lock, irqflags); ++ return o; ++} ++ ++/* If the context is not preemptible, select the service affine to the ++ * current cpu. Otherwise, "select any". ++ */ ++static inline struct dpaa2_io *_service_select(struct dpaa2_io *d) ++{ ++ struct dpaa2_io *temp = d; ++ ++ if (likely(!preemptible())) { ++ d = service_select_by_cpu(d, smp_processor_id()); ++ if (likely(d)) ++ return d; ++ } ++ return service_select_any(temp); ++} ++ ++/**********************/ ++/* Exported functions */ ++/**********************/ ++ ++struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc) ++{ ++ struct dpaa2_io *ret = kmalloc(sizeof(*ret), GFP_KERNEL); ++ struct dpaa2_io_object *o = &ret->object; ++ ++ if (!ret) ++ return NULL; ++ ret->magic = MAGIC_OBJECT; ++ atomic_set(&ret->refs, 1); ++ o->dpio_desc = *desc; ++ o->swp_desc.cena_bar = o->dpio_desc.regs_cena; ++ o->swp_desc.cinh_bar = o->dpio_desc.regs_cinh; ++ o->swp_desc.qman_version = o->dpio_desc.qman_version; ++ o->swp = qbman_swp_init(&o->swp_desc); ++ o->service = NULL; ++ if (!o->swp) { ++ kfree(ret); ++ return NULL; ++ } ++ INIT_LIST_HEAD(&o->node); ++ spin_lock_init(&o->lock_mgmt_cmd); ++ spin_lock_init(&o->lock_notifications); ++ INIT_LIST_HEAD(&o->notifications); ++ if (!o->dpio_desc.has_irq) ++ qbman_swp_interrupt_set_vanish(o->swp, 0xffffffff); ++ else { ++ /* For now only enable DQRR interrupts */ ++ qbman_swp_interrupt_set_trigger(o->swp, ++ QBMAN_SWP_INTERRUPT_DQRI); ++ } ++ qbman_swp_interrupt_clear_status(o->swp, 0xffffffff); ++ if (o->dpio_desc.receives_notifications) ++ qbman_swp_push_set(o->swp, 0, 1); ++ return ret; ++} ++EXPORT_SYMBOL(dpaa2_io_create); ++ ++struct dpaa2_io *dpaa2_io_create_service(void) ++{ ++ struct dpaa2_io *ret = kmalloc(sizeof(*ret), GFP_KERNEL); ++ ++ if (ret) ++ service_init(ret, 0); ++ return ret; ++} ++EXPORT_SYMBOL(dpaa2_io_create_service); ++ ++struct dpaa2_io *dpaa2_io_default_service(void) ++{ ++ atomic_inc(&def_serv.refs); ++ return &def_serv; ++} ++EXPORT_SYMBOL(dpaa2_io_default_service); ++ ++void dpaa2_io_down(struct dpaa2_io *d) ++{ ++ if (!atomic_dec_and_test(&d->refs)) ++ return; ++ if (d->magic == MAGIC_SERVICE) { ++ BUG_ON(!list_empty(&d->service.list)); ++ if (d->service.is_defservice) ++ /* avoid the kfree()! */ ++ return; ++ } else { ++ BUG_ON(d->magic != MAGIC_OBJECT); ++ BUG_ON(d->object.service); ++ BUG_ON(!list_empty(&d->object.notifications)); ++ } ++ kfree(d); ++} ++EXPORT_SYMBOL(dpaa2_io_down); ++ ++int dpaa2_io_service_add(struct dpaa2_io *s, struct dpaa2_io *o) ++{ ++ struct dpaa2_io_service *ss = &s->service; ++ struct dpaa2_io_object *oo = &o->object; ++ int res = -EINVAL; ++ ++ if ((s->magic != MAGIC_SERVICE) || (o->magic != MAGIC_OBJECT)) ++ return res; ++ atomic_inc(&o->refs); ++ atomic_inc(&s->refs); ++ spin_lock(&ss->lock); ++ /* 'obj' must not already be associated with a service */ ++ if (!oo->service) { ++ oo->service = s; ++ list_add(&oo->node, &ss->list); ++ if (oo->dpio_desc.receives_notifications) { ++ cpumask_set_cpu(oo->dpio_desc.cpu, ++ &ss->cpus_notifications); ++ /* Update the fast-access array */ ++ ss->objects_by_cpu[oo->dpio_desc.cpu] = ++ container_of(oo, struct dpaa2_io, object); ++ } ++ if (oo->dpio_desc.stash_affinity) ++ cpumask_set_cpu(oo->dpio_desc.cpu, ++ &ss->cpus_stashing); ++ if (!oo->dpio_desc.stash_affinity) ++ ss->has_nonaffine = 1; ++ /* success */ ++ res = 0; ++ } ++ spin_unlock(&ss->lock); ++ if (res) { ++ dpaa2_io_down(s); ++ dpaa2_io_down(o); ++ } ++ return res; ++} ++EXPORT_SYMBOL(dpaa2_io_service_add); ++ ++int dpaa2_io_get_descriptor(struct dpaa2_io *obj, struct dpaa2_io_desc *desc) ++{ ++ if (obj->magic == MAGIC_SERVICE) ++ return -EINVAL; ++ BUG_ON(obj->magic != MAGIC_OBJECT); ++ *desc = obj->object.dpio_desc; ++ return 0; ++} ++EXPORT_SYMBOL(dpaa2_io_get_descriptor); ++ ++#define DPAA_POLL_MAX 32 ++ ++int dpaa2_io_poll(struct dpaa2_io *obj) ++{ ++ const struct dpaa2_dq *dq; ++ struct qbman_swp *swp; ++ int max = 0; ++ ++ if (obj->magic != MAGIC_OBJECT) ++ return -EINVAL; ++ swp = obj->object.swp; ++ dq = qbman_swp_dqrr_next(swp); ++ while (dq) { ++ if (qbman_result_is_SCN(dq)) { ++ struct dpaa2_io_notification_ctx *ctx; ++ uint64_t q64; ++ ++ q64 = qbman_result_SCN_ctx(dq); ++ ctx = (void *)q64; ++ ctx->cb(ctx); ++ } else ++ pr_crit("Unrecognised/ignored DQRR entry\n"); ++ qbman_swp_dqrr_consume(swp, dq); ++ ++max; ++ if (max > DPAA_POLL_MAX) ++ return 0; ++ dq = qbman_swp_dqrr_next(swp); ++ } ++ return 0; ++} ++EXPORT_SYMBOL(dpaa2_io_poll); ++ ++int dpaa2_io_irq(struct dpaa2_io *obj) ++{ ++ struct qbman_swp *swp; ++ uint32_t status; ++ ++ if (obj->magic != MAGIC_OBJECT) ++ return -EINVAL; ++ swp = obj->object.swp; ++ status = qbman_swp_interrupt_read_status(swp); ++ if (!status) ++ return IRQ_NONE; ++ dpaa2_io_poll(obj); ++ qbman_swp_interrupt_clear_status(swp, status); ++ qbman_swp_interrupt_set_inhibit(swp, 0); ++ return IRQ_HANDLED; ++} ++EXPORT_SYMBOL(dpaa2_io_irq); ++ ++int dpaa2_io_pause_poll(struct dpaa2_io *obj) ++{ ++ UNIMPLEMENTED(); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(dpaa2_io_pause_poll); ++ ++int dpaa2_io_resume_poll(struct dpaa2_io *obj) ++{ ++ UNIMPLEMENTED(); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(dpaa2_io_resume_poll); ++ ++void dpaa2_io_service_notifications(struct dpaa2_io *s, cpumask_t *mask) ++{ ++ struct dpaa2_io_service *ss = &s->service; ++ ++ BUG_ON(s->magic != MAGIC_SERVICE); ++ cpumask_copy(mask, &ss->cpus_notifications); ++} ++EXPORT_SYMBOL(dpaa2_io_service_notifications); ++ ++void dpaa2_io_service_stashing(struct dpaa2_io *s, cpumask_t *mask) ++{ ++ struct dpaa2_io_service *ss = &s->service; ++ ++ BUG_ON(s->magic != MAGIC_SERVICE); ++ cpumask_copy(mask, &ss->cpus_stashing); ++} ++EXPORT_SYMBOL(dpaa2_io_service_stashing); ++ ++int dpaa2_io_service_has_nonaffine(struct dpaa2_io *s) ++{ ++ struct dpaa2_io_service *ss = &s->service; ++ ++ BUG_ON(s->magic != MAGIC_SERVICE); ++ return ss->has_nonaffine; ++} ++EXPORT_SYMBOL(dpaa2_io_service_has_nonaffine); ++ ++int dpaa2_io_service_register(struct dpaa2_io *d, ++ struct dpaa2_io_notification_ctx *ctx) ++{ ++ unsigned long irqflags; ++ ++ d = service_select_by_cpu(d, ctx->desired_cpu); ++ if (!d) ++ return -ENODEV; ++ ctx->dpio_id = d->object.dpio_desc.dpio_id; ++ ctx->qman64 = (uint64_t)ctx; ++ ctx->dpio_private = d; ++ spin_lock_irqsave(&d->object.lock_notifications, irqflags); ++ list_add(&ctx->node, &d->object.notifications); ++ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); ++ if (ctx->is_cdan) ++ /* Enable the generation of CDAN notifications */ ++ qbman_swp_CDAN_set_context_enable(d->object.swp, ++ (uint16_t)ctx->id, ++ ctx->qman64); ++ return 0; ++} ++EXPORT_SYMBOL(dpaa2_io_service_register); ++ ++int dpaa2_io_service_deregister(struct dpaa2_io *service, ++ struct dpaa2_io_notification_ctx *ctx) ++{ ++ struct dpaa2_io *d = ctx->dpio_private; ++ unsigned long irqflags; ++ ++ if (!service) ++ service = &def_serv; ++ BUG_ON((service != d) && (service != d->object.service)); ++ if (ctx->is_cdan) ++ qbman_swp_CDAN_disable(d->object.swp, ++ (uint16_t)ctx->id); ++ spin_lock_irqsave(&d->object.lock_notifications, irqflags); ++ list_del(&ctx->node); ++ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); ++ return 0; ++} ++EXPORT_SYMBOL(dpaa2_io_service_deregister); ++ ++int dpaa2_io_service_rearm(struct dpaa2_io *d, ++ struct dpaa2_io_notification_ctx *ctx) ++{ ++ unsigned long irqflags; ++ int err; ++ ++ d = _service_select(d); ++ if (!d) ++ return -ENODEV; ++ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); ++ if (ctx->is_cdan) ++ err = qbman_swp_CDAN_enable(d->object.swp, (uint16_t)ctx->id); ++ else ++ err = qbman_swp_fq_schedule(d->object.swp, ctx->id); ++ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); ++ return err; ++} ++EXPORT_SYMBOL(dpaa2_io_service_rearm); ++ ++int dpaa2_io_from_registration(struct dpaa2_io_notification_ctx *ctx, ++ struct dpaa2_io **io) ++{ ++ struct dpaa2_io_notification_ctx *tmp; ++ struct dpaa2_io *d = ctx->dpio_private; ++ unsigned long irqflags; ++ int ret = 0; ++ ++ BUG_ON(d->magic != MAGIC_OBJECT); ++ /* Iterate the notifications associated with 'd' looking for a match. If ++ * not, we've been passed an unregistered ctx! */ ++ spin_lock_irqsave(&d->object.lock_notifications, irqflags); ++ list_for_each_entry(tmp, &d->object.notifications, node) ++ if (tmp == ctx) ++ goto found; ++ ret = -EINVAL; ++found: ++ spin_unlock_irqrestore(&d->object.lock_notifications, irqflags); ++ if (!ret) { ++ atomic_inc(&d->refs); ++ *io = d; ++ } ++ return ret; ++} ++EXPORT_SYMBOL(dpaa2_io_from_registration); ++ ++int dpaa2_io_service_get_persistent(struct dpaa2_io *service, int cpu, ++ struct dpaa2_io **ret) ++{ ++ if (cpu == -1) ++ *ret = service_select_any(service); ++ else ++ *ret = service_select_by_cpu(service, cpu); ++ if (*ret) { ++ atomic_inc(&(*ret)->refs); ++ return 0; ++ } ++ return -ENODEV; ++} ++EXPORT_SYMBOL(dpaa2_io_service_get_persistent); ++ ++int dpaa2_io_service_pull_fq(struct dpaa2_io *d, uint32_t fqid, ++ struct dpaa2_io_store *s) ++{ ++ struct qbman_pull_desc pd; ++ int err; ++ ++ qbman_pull_desc_clear(&pd); ++ qbman_pull_desc_set_storage(&pd, s->vaddr, s->paddr, 1); ++ qbman_pull_desc_set_numframes(&pd, (uint8_t)s->max); ++ qbman_pull_desc_set_fq(&pd, fqid); ++ d = _service_select(d); ++ if (!d) ++ return -ENODEV; ++ s->swp = d->object.swp; ++ err = qbman_swp_pull(d->object.swp, &pd); ++ if (err) ++ s->swp = NULL; ++ return err; ++} ++EXPORT_SYMBOL(dpaa2_io_service_pull_fq); ++ ++int dpaa2_io_service_pull_channel(struct dpaa2_io *d, uint32_t channelid, ++ struct dpaa2_io_store *s) ++{ ++ struct qbman_pull_desc pd; ++ int err; ++ ++ qbman_pull_desc_clear(&pd); ++ qbman_pull_desc_set_storage(&pd, s->vaddr, s->paddr, 1); ++ qbman_pull_desc_set_numframes(&pd, (uint8_t)s->max); ++ qbman_pull_desc_set_channel(&pd, channelid, qbman_pull_type_prio); ++ d = _service_select(d); ++ if (!d) ++ return -ENODEV; ++ s->swp = d->object.swp; ++ err = qbman_swp_pull(d->object.swp, &pd); ++ if (err) ++ s->swp = NULL; ++ return err; ++} ++EXPORT_SYMBOL(dpaa2_io_service_pull_channel); ++ ++int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, ++ uint32_t fqid, ++ const struct dpaa2_fd *fd) ++{ ++ struct qbman_eq_desc ed; ++ ++ d = _service_select(d); ++ if (!d) ++ return -ENODEV; ++ qbman_eq_desc_clear(&ed); ++ qbman_eq_desc_set_no_orp(&ed, 0); ++ qbman_eq_desc_set_fq(&ed, fqid); ++ return qbman_swp_enqueue(d->object.swp, &ed, ++ (const struct qbman_fd *)fd); ++} ++EXPORT_SYMBOL(dpaa2_io_service_enqueue_fq); ++ ++int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, ++ uint32_t qdid, uint8_t prio, uint16_t qdbin, ++ const struct dpaa2_fd *fd) ++{ ++ struct qbman_eq_desc ed; ++ ++ d = _service_select(d); ++ if (!d) ++ return -ENODEV; ++ qbman_eq_desc_clear(&ed); ++ qbman_eq_desc_set_no_orp(&ed, 0); ++ qbman_eq_desc_set_qd(&ed, qdid, qdbin, prio); ++ return qbman_swp_enqueue(d->object.swp, &ed, ++ (const struct qbman_fd *)fd); ++} ++EXPORT_SYMBOL(dpaa2_io_service_enqueue_qd); ++ ++int dpaa2_io_service_release(struct dpaa2_io *d, ++ uint32_t bpid, ++ const uint64_t *buffers, ++ unsigned int num_buffers) ++{ ++ struct qbman_release_desc rd; ++ ++ d = _service_select(d); ++ if (!d) ++ return -ENODEV; ++ qbman_release_desc_clear(&rd); ++ qbman_release_desc_set_bpid(&rd, bpid); ++ return qbman_swp_release(d->object.swp, &rd, buffers, num_buffers); ++} ++EXPORT_SYMBOL(dpaa2_io_service_release); ++ ++int dpaa2_io_service_acquire(struct dpaa2_io *d, ++ uint32_t bpid, ++ uint64_t *buffers, ++ unsigned int num_buffers) ++{ ++ unsigned long irqflags; ++ int err; ++ ++ d = _service_select(d); ++ if (!d) ++ return -ENODEV; ++ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); ++ err = qbman_swp_acquire(d->object.swp, bpid, buffers, num_buffers); ++ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); ++ return err; ++} ++EXPORT_SYMBOL(dpaa2_io_service_acquire); ++ ++struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, ++ struct device *dev) ++{ ++ struct dpaa2_io_store *ret = kmalloc(sizeof(*ret), GFP_KERNEL); ++ size_t size; ++ ++ BUG_ON(!max_frames || (max_frames > 16)); ++ if (!ret) ++ return NULL; ++ ret->max = max_frames; ++ size = max_frames * sizeof(struct dpaa2_dq) + 64; ++ ret->alloced_addr = kmalloc(size, GFP_KERNEL); ++ if (!ret->alloced_addr) { ++ kfree(ret); ++ return NULL; ++ } ++ ret->vaddr = PTR_ALIGN(ret->alloced_addr, 64); ++ ret->paddr = dma_map_single(dev, ret->vaddr, ++ sizeof(struct dpaa2_dq) * max_frames, ++ DMA_FROM_DEVICE); ++ if (dma_mapping_error(dev, ret->paddr)) { ++ kfree(ret->alloced_addr); ++ kfree(ret); ++ return NULL; ++ } ++ ret->idx = 0; ++ ret->dev = dev; ++ return ret; ++} ++EXPORT_SYMBOL(dpaa2_io_store_create); ++ ++void dpaa2_io_store_destroy(struct dpaa2_io_store *s) ++{ ++ dma_unmap_single(s->dev, s->paddr, sizeof(struct dpaa2_dq) * s->max, ++ DMA_FROM_DEVICE); ++ kfree(s->alloced_addr); ++ kfree(s); ++} ++EXPORT_SYMBOL(dpaa2_io_store_destroy); ++ ++struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last) ++{ ++ int match; ++ struct dpaa2_dq *ret = &s->vaddr[s->idx]; ++ ++ match = qbman_result_has_new_result(s->swp, ret); ++ if (!match) { ++ *is_last = 0; ++ return NULL; ++ } ++ BUG_ON(!qbman_result_is_DQ(ret)); ++ s->idx++; ++ if (dpaa2_dq_is_pull_complete(ret)) { ++ *is_last = 1; ++ s->idx = 0; ++ /* If we get an empty dequeue result to terminate a zero-results ++ * vdqcr, return NULL to the caller rather than expecting him to ++ * check non-NULL results every time. */ ++ if (!(dpaa2_dq_flags(ret) & DPAA2_DQ_STAT_VALIDFRAME)) ++ ret = NULL; ++ } else ++ *is_last = 0; ++ return ret; ++} ++EXPORT_SYMBOL(dpaa2_io_store_next); ++ ++#ifdef CONFIG_FSL_QBMAN_DEBUG ++int dpaa2_io_query_fq_count(struct dpaa2_io *d, uint32_t fqid, ++ uint32_t *fcnt, uint32_t *bcnt) ++{ ++ struct qbman_attr state; ++ struct qbman_swp *swp; ++ unsigned long irqflags; ++ int ret; ++ ++ d = service_select_any(d); ++ if (!d) ++ return -ENODEV; ++ ++ swp = d->object.swp; ++ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); ++ ret = qbman_fq_query_state(swp, fqid, &state); ++ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); ++ if (ret) ++ return ret; ++ *fcnt = qbman_fq_state_frame_count(&state); ++ *bcnt = qbman_fq_state_byte_count(&state); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpaa2_io_query_fq_count); ++ ++int dpaa2_io_query_bp_count(struct dpaa2_io *d, uint32_t bpid, ++ uint32_t *num) ++{ ++ struct qbman_attr state; ++ struct qbman_swp *swp; ++ unsigned long irqflags; ++ int ret; ++ ++ d = service_select_any(d); ++ if (!d) ++ return -ENODEV; ++ ++ swp = d->object.swp; ++ spin_lock_irqsave(&d->object.lock_mgmt_cmd, irqflags); ++ ret = qbman_bp_query(swp, bpid, &state); ++ spin_unlock_irqrestore(&d->object.lock_mgmt_cmd, irqflags); ++ if (ret) ++ return ret; ++ *num = qbman_bp_info_num_free_bufs(&state); ++ return 0; ++} ++EXPORT_SYMBOL(dpaa2_io_query_bp_count); ++ ++#endif ++ ++/* module init/exit hooks called from dpio-drv.c. These are declared in ++ * dpio-drv.h. ++ */ ++int dpaa2_io_service_driver_init(void) ++{ ++ service_init(&def_serv, 1); ++ return 0; ++} ++ ++void dpaa2_io_service_driver_exit(void) ++{ ++ if (atomic_read(&def_serv.refs) != 1) ++ pr_err("default DPIO service leaves dangling DPIO objects!\n"); ++} +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio.h +@@ -0,0 +1,460 @@ ++/* Copyright 2013-2015 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 the above-listed copyright holders nor the ++ * names of any 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDERS OR CONTRIBUTORS 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_DPIO_H ++#define __FSL_DPIO_H ++ ++/* Data Path I/O Portal API ++ * Contains initialization APIs and runtime control APIs for DPIO ++ */ ++ ++struct fsl_mc_io; ++ ++/** ++ * dpio_open() - Open a control session for the specified object ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @dpio_id: DPIO unique ID ++ * @token: Returned token; use in subsequent API calls ++ * ++ * This function can be used to open a control session for an ++ * already created object; an object may have been declared in ++ * the DPL or by calling the dpio_create() function. ++ * This function returns a unique authentication token, ++ * associated with the specific object ID and the specific MC ++ * portal; this token must be used in all subsequent commands for ++ * this specific object. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_open(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ int dpio_id, ++ uint16_t *token); ++ ++/** ++ * dpio_close() - Close the control session of the object ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_close(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token); ++ ++/** ++ * enum dpio_channel_mode - DPIO notification channel mode ++ * @DPIO_NO_CHANNEL: No support for notification channel ++ * @DPIO_LOCAL_CHANNEL: Notifications on data availability can be received by a ++ * dedicated channel in the DPIO; user should point the queue's ++ * destination in the relevant interface to this DPIO ++ */ ++enum dpio_channel_mode { ++ DPIO_NO_CHANNEL = 0, ++ DPIO_LOCAL_CHANNEL = 1, ++}; ++ ++/** ++ * struct dpio_cfg - Structure representing DPIO configuration ++ * @channel_mode: Notification channel mode ++ * @num_priorities: Number of priorities for the notification channel (1-8); ++ * relevant only if 'channel_mode = DPIO_LOCAL_CHANNEL' ++ */ ++struct dpio_cfg { ++ enum dpio_channel_mode channel_mode; ++ uint8_t num_priorities; ++}; ++ ++/** ++ * dpio_create() - Create the DPIO object. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @cfg: Configuration structure ++ * @token: Returned token; use in subsequent API calls ++ * ++ * Create the DPIO object, allocate required resources and ++ * perform required initialization. ++ * ++ * The object can be created either by declaring it in the ++ * DPL file, or by calling this function. ++ * ++ * This function returns a unique authentication token, ++ * associated with the specific object ID and the specific MC ++ * portal; this token must be used in all subsequent calls to ++ * this specific object. For objects that are created using the ++ * DPL file, call dpio_open() function to get an authentication ++ * token first. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_create(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ const struct dpio_cfg *cfg, ++ uint16_t *token); ++ ++/** ++ * dpio_destroy() - Destroy the DPIO object and release all its resources. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * ++ * Return: '0' on Success; Error code otherwise ++ */ ++int dpio_destroy(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token); ++ ++/** ++ * dpio_enable() - Enable the DPIO, allow I/O portal operations. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * ++ * Return: '0' on Success; Error code otherwise ++ */ ++int dpio_enable(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token); ++ ++/** ++ * dpio_disable() - Disable the DPIO, stop any I/O portal operation. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * ++ * Return: '0' on Success; Error code otherwise ++ */ ++int dpio_disable(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token); ++ ++/** ++ * dpio_is_enabled() - Check if the DPIO is enabled. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @en: Returns '1' if object is enabled; '0' otherwise ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_is_enabled(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ int *en); ++ ++/** ++ * dpio_reset() - Reset the DPIO, returns the object to initial state. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_reset(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token); ++ ++/** ++ * dpio_set_stashing_destination() - Set the stashing destination. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @sdest: stashing destination value ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_set_stashing_destination(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t sdest); ++ ++/** ++ * dpio_get_stashing_destination() - Get the stashing destination.. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @sdest: Returns the stashing destination value ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_get_stashing_destination(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t *sdest); ++ ++/** ++ * dpio_add_static_dequeue_channel() - Add a static dequeue channel. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @dpcon_id: DPCON object ID ++ * @channel_index: Returned channel index to be used in qbman API ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_add_static_dequeue_channel(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ int dpcon_id, ++ uint8_t *channel_index); ++ ++/** ++ * dpio_remove_static_dequeue_channel() - Remove a static dequeue channel. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @dpcon_id: DPCON object ID ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_remove_static_dequeue_channel(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ int dpcon_id); ++ ++/** ++ * DPIO IRQ Index and Events ++ */ ++ ++/** ++ * Irq software-portal index ++ */ ++#define DPIO_IRQ_SWP_INDEX 0 ++ ++/** ++ * struct dpio_irq_cfg - IRQ configuration ++ * @addr: Address that must be written to signal a message-based interrupt ++ * @val: Value to write into irq_addr address ++ * @irq_num: A user defined number associated with this IRQ ++ */ ++struct dpio_irq_cfg { ++ uint64_t addr; ++ uint32_t val; ++ int irq_num; ++}; ++ ++/** ++ * dpio_set_irq() - Set IRQ information for the DPIO to trigger an interrupt. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @irq_index: Identifies the interrupt index to configure ++ * @irq_cfg: IRQ configuration ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_set_irq(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ struct dpio_irq_cfg *irq_cfg); ++ ++/** ++ * dpio_get_irq() - Get IRQ information from the DPIO. ++ * ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @irq_index: The interrupt index to configure ++ * @type: Interrupt type: 0 represents message interrupt ++ * type (both irq_addr and irq_val are valid) ++ * @irq_cfg: IRQ attributes ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_get_irq(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ int *type, ++ struct dpio_irq_cfg *irq_cfg); ++ ++/** ++ * dpio_set_irq_enable() - Set overall interrupt state. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @irq_index: The interrupt index to configure ++ * @en: Interrupt state - enable = 1, disable = 0 ++ * ++ * Allows GPP software to control when interrupts are generated. ++ * Each interrupt can have up to 32 causes. The enable/disable control's the ++ * overall interrupt state. if the interrupt is disabled no causes will cause ++ * an interrupt. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_set_irq_enable(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint8_t en); ++ ++/** ++ * dpio_get_irq_enable() - Get overall interrupt state ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @irq_index: The interrupt index to configure ++ * @en: Returned interrupt state - enable = 1, disable = 0 ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_get_irq_enable(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint8_t *en); ++ ++/** ++ * dpio_set_irq_mask() - Set interrupt mask. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @irq_index: The interrupt index to configure ++ * @mask: event mask to trigger interrupt; ++ * each bit: ++ * 0 = ignore event ++ * 1 = consider event for asserting IRQ ++ * ++ * Every interrupt can have up to 32 causes and the interrupt model supports ++ * masking/unmasking each cause independently ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_set_irq_mask(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint32_t mask); ++ ++/** ++ * dpio_get_irq_mask() - Get interrupt mask. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @irq_index: The interrupt index to configure ++ * @mask: Returned event mask to trigger interrupt ++ * ++ * Every interrupt can have up to 32 causes and the interrupt model supports ++ * masking/unmasking each cause independently ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_get_irq_mask(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint32_t *mask); ++ ++/** ++ * dpio_get_irq_status() - Get the current status of any pending interrupts. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @irq_index: The interrupt index to configure ++ * @status: Returned interrupts status - one bit per cause: ++ * 0 = no interrupt pending ++ * 1 = interrupt pending ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_get_irq_status(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint32_t *status); ++ ++/** ++ * dpio_clear_irq_status() - Clear a pending interrupt's status ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @irq_index: The interrupt index to configure ++ * @status: bits to clear (W1C) - one bit per cause: ++ * 0 = don't change ++ * 1 = clear status bit ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpio_clear_irq_status(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ uint8_t irq_index, ++ uint32_t status); ++ ++/** ++ * struct dpio_attr - Structure representing DPIO attributes ++ * @id: DPIO object ID ++ * @version: DPIO version ++ * @qbman_portal_ce_offset: offset of the software portal cache-enabled area ++ * @qbman_portal_ci_offset: offset of the software portal cache-inhibited area ++ * @qbman_portal_id: Software portal ID ++ * @channel_mode: Notification channel mode ++ * @num_priorities: Number of priorities for the notification channel (1-8); ++ * relevant only if 'channel_mode = DPIO_LOCAL_CHANNEL' ++ * @qbman_version: QBMAN version ++ */ ++struct dpio_attr { ++ int id; ++ /** ++ * struct version - DPIO version ++ * @major: DPIO major version ++ * @minor: DPIO minor version ++ */ ++ struct { ++ uint16_t major; ++ uint16_t minor; ++ } version; ++ uint64_t qbman_portal_ce_offset; ++ uint64_t qbman_portal_ci_offset; ++ uint16_t qbman_portal_id; ++ enum dpio_channel_mode channel_mode; ++ uint8_t num_priorities; ++ uint32_t qbman_version; ++}; ++ ++/** ++ * dpio_get_attributes() - Retrieve DPIO attributes ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPIO object ++ * @attr: Returned object's attributes ++ * ++ * Return: '0' on Success; Error code otherwise ++ */ ++int dpio_get_attributes(struct fsl_mc_io *mc_io, ++ uint32_t cmd_flags, ++ uint16_t token, ++ struct dpio_attr *attr); ++#endif /* __FSL_DPIO_H */ +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/fsl_dpio_cmd.h +@@ -0,0 +1,184 @@ ++/* Copyright 2013-2015 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 the above-listed copyright holders nor the ++ * names of any 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDERS OR CONTRIBUTORS 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_DPIO_CMD_H ++#define _FSL_DPIO_CMD_H ++ ++/* DPIO Version */ ++#define DPIO_VER_MAJOR 3 ++#define DPIO_VER_MINOR 2 ++ ++/* Command IDs */ ++#define DPIO_CMDID_CLOSE 0x800 ++#define DPIO_CMDID_OPEN 0x803 ++#define DPIO_CMDID_CREATE 0x903 ++#define DPIO_CMDID_DESTROY 0x900 ++ ++#define DPIO_CMDID_ENABLE 0x002 ++#define DPIO_CMDID_DISABLE 0x003 ++#define DPIO_CMDID_GET_ATTR 0x004 ++#define DPIO_CMDID_RESET 0x005 ++#define DPIO_CMDID_IS_ENABLED 0x006 ++ ++#define DPIO_CMDID_SET_IRQ 0x010 ++#define DPIO_CMDID_GET_IRQ 0x011 ++#define DPIO_CMDID_SET_IRQ_ENABLE 0x012 ++#define DPIO_CMDID_GET_IRQ_ENABLE 0x013 ++#define DPIO_CMDID_SET_IRQ_MASK 0x014 ++#define DPIO_CMDID_GET_IRQ_MASK 0x015 ++#define DPIO_CMDID_GET_IRQ_STATUS 0x016 ++#define DPIO_CMDID_CLEAR_IRQ_STATUS 0x017 ++ ++#define DPIO_CMDID_SET_STASHING_DEST 0x120 ++#define DPIO_CMDID_GET_STASHING_DEST 0x121 ++#define DPIO_CMDID_ADD_STATIC_DEQUEUE_CHANNEL 0x122 ++#define DPIO_CMDID_REMOVE_STATIC_DEQUEUE_CHANNEL 0x123 ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_OPEN(cmd, dpio_id) \ ++ MC_CMD_OP(cmd, 0, 0, 32, int, dpio_id) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_CREATE(cmd, cfg) \ ++do { \ ++ MC_CMD_OP(cmd, 0, 16, 2, enum dpio_channel_mode, \ ++ cfg->channel_mode);\ ++ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, cfg->num_priorities);\ ++} while (0) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_RSP_IS_ENABLED(cmd, en) \ ++ MC_RSP_OP(cmd, 0, 0, 1, int, en) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_SET_IRQ(cmd, irq_index, irq_cfg) \ ++do { \ ++ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\ ++ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_cfg->val);\ ++ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr);\ ++ MC_CMD_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ ++} while (0) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_GET_IRQ(cmd, irq_index) \ ++ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_RSP_GET_IRQ(cmd, type, irq_cfg) \ ++do { \ ++ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_cfg->val); \ ++ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_cfg->addr); \ ++ MC_RSP_OP(cmd, 2, 0, 32, int, irq_cfg->irq_num); \ ++ MC_RSP_OP(cmd, 2, 32, 32, int, type); \ ++} while (0) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \ ++do { \ ++ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \ ++ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ ++} while (0) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_GET_IRQ_ENABLE(cmd, irq_index) \ ++ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_RSP_GET_IRQ_ENABLE(cmd, en) \ ++ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \ ++do { \ ++ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask); \ ++ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ ++} while (0) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_GET_IRQ_MASK(cmd, irq_index) \ ++ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_RSP_GET_IRQ_MASK(cmd, mask) \ ++ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_GET_IRQ_STATUS(cmd, irq_index, status) \ ++do { \ ++ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status);\ ++ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ ++} while (0) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_RSP_GET_IRQ_STATUS(cmd, status) \ ++ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \ ++do { \ ++ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \ ++ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\ ++} while (0) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_RSP_GET_ATTR(cmd, attr) \ ++do { \ ++ MC_RSP_OP(cmd, 0, 0, 32, int, attr->id);\ ++ MC_RSP_OP(cmd, 0, 32, 16, uint16_t, attr->qbman_portal_id);\ ++ MC_RSP_OP(cmd, 0, 48, 8, uint8_t, attr->num_priorities);\ ++ MC_RSP_OP(cmd, 0, 56, 4, enum dpio_channel_mode, attr->channel_mode);\ ++ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, attr->qbman_portal_ce_offset);\ ++ MC_RSP_OP(cmd, 2, 0, 64, uint64_t, attr->qbman_portal_ci_offset);\ ++ MC_RSP_OP(cmd, 3, 0, 16, uint16_t, attr->version.major);\ ++ MC_RSP_OP(cmd, 3, 16, 16, uint16_t, attr->version.minor);\ ++ MC_RSP_OP(cmd, 3, 32, 32, uint32_t, attr->qbman_version);\ ++} while (0) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_SET_STASHING_DEST(cmd, sdest) \ ++ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, sdest) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_RSP_GET_STASHING_DEST(cmd, sdest) \ ++ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, sdest) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_ADD_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id) \ ++ MC_CMD_OP(cmd, 0, 0, 32, int, dpcon_id) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_RSP_ADD_STATIC_DEQUEUE_CHANNEL(cmd, channel_index) \ ++ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, channel_index) ++ ++/* cmd, param, offset, width, type, arg_name */ ++#define DPIO_CMD_REMOVE_STATIC_DEQUEUE_CHANNEL(cmd, dpcon_id) \ ++ MC_CMD_OP(cmd, 0, 0, 32, int, dpcon_id) ++#endif /* _FSL_DPIO_CMD_H */ +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_base.h +@@ -0,0 +1,123 @@ ++/* Copyright (C) 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 _FSL_QBMAN_BASE_H ++#define _FSL_QBMAN_BASE_H ++ ++/** ++ * struct qbman_block_desc - qbman block descriptor structure ++ * ++ * Descriptor for a QBMan instance on the SoC. On partitions/targets that do not ++ * control this QBMan instance, these values may simply be place-holders. The ++ * idea is simply that we be able to distinguish between them, eg. so that SWP ++ * descriptors can identify which QBMan instance they belong to. ++ */ ++struct qbman_block_desc { ++ void *ccsr_reg_bar; /* CCSR register map */ ++ int irq_rerr; /* Recoverable error interrupt line */ ++ int irq_nrerr; /* Non-recoverable error interrupt line */ ++}; ++ ++/** ++ * struct qbman_swp_desc - qbman software portal descriptor structure ++ * ++ * Descriptor for a QBMan software portal, expressed in terms that make sense to ++ * the user context. Ie. on MC, this information is likely to be true-physical, ++ * and instantiated statically at compile-time. On GPP, this information is ++ * likely to be obtained via "discovery" over a partition's "layerscape bus" ++ * (ie. in response to a MC portal command), and would take into account any ++ * virtualisation of the GPP user's address space and/or interrupt numbering. ++ */ ++struct qbman_swp_desc { ++ const struct qbman_block_desc *block; /* The QBMan instance */ ++ void *cena_bar; /* Cache-enabled portal register map */ ++ void *cinh_bar; /* Cache-inhibited portal register map */ ++ uint32_t qman_version; ++}; ++ ++/* Driver object for managing a QBMan portal */ ++struct qbman_swp; ++ ++/** ++ * struct qbman_fd - basci structure for qbman frame descriptor ++ * ++ * Place-holder for FDs, we represent it via the simplest form that we need for ++ * now. Different overlays may be needed to support different options, etc. (It ++ * is impractical to define One True Struct, because the resulting encoding ++ * routines (lots of read-modify-writes) would be worst-case performance whether ++ * or not circumstances required them.) ++ * ++ * Note, as with all data-structures exchanged between software and hardware (be ++ * they located in the portal register map or DMA'd to and from main-memory), ++ * the driver ensures that the caller of the driver API sees the data-structures ++ * in host-endianness. "struct qbman_fd" is no exception. The 32-bit words ++ * contained within this structure are represented in host-endianness, even if ++ * hardware always treats them as little-endian. As such, if any of these fields ++ * are interpreted in a binary (rather than numerical) fashion by hardware ++ * blocks (eg. accelerators), then the user should be careful. We illustrate ++ * with an example; ++ * ++ * Suppose the desired behaviour of an accelerator is controlled by the "frc" ++ * field of the FDs that are sent to it. Suppose also that the behaviour desired ++ * by the user corresponds to an "frc" value which is expressed as the literal ++ * sequence of bytes 0xfe, 0xed, 0xab, and 0xba. So "frc" should be the 32-bit ++ * value in which 0xfe is the first byte and 0xba is the last byte, and as ++ * hardware is little-endian, this amounts to a 32-bit "value" of 0xbaabedfe. If ++ * the software is little-endian also, this can simply be achieved by setting ++ * frc=0xbaabedfe. On the other hand, if software is big-endian, it should set ++ * frc=0xfeedabba! The best away of avoiding trouble with this sort of thing is ++ * to treat the 32-bit words as numerical values, in which the offset of a field ++ * from the beginning of the first byte (as required or generated by hardware) ++ * is numerically encoded by a left-shift (ie. by raising the field to a ++ * corresponding power of 2). Ie. in the current example, software could set ++ * "frc" in the following way, and it would work correctly on both little-endian ++ * and big-endian operation; ++ * fd.frc = (0xfe << 0) | (0xed << 8) | (0xab << 16) | (0xba << 24); ++ */ ++struct qbman_fd { ++ union { ++ uint32_t words[8]; ++ struct qbman_fd_simple { ++ uint32_t addr_lo; ++ uint32_t addr_hi; ++ uint32_t len; ++ /* offset in the MS 16 bits, BPID in the LS 16 bits */ ++ uint32_t bpid_offset; ++ uint32_t frc; /* frame context */ ++ /* "err", "va", "cbmt", "asal", [...] */ ++ uint32_t ctrl; ++ /* flow context */ ++ uint32_t flc_lo; ++ uint32_t flc_hi; ++ } simple; ++ }; ++}; ++ ++#endif /* !_FSL_QBMAN_BASE_H */ +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/fsl_qbman_portal.h +@@ -0,0 +1,753 @@ ++/* Copyright (C) 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 _FSL_QBMAN_PORTAL_H ++#define _FSL_QBMAN_PORTAL_H ++ ++#include "fsl_qbman_base.h" ++ ++/** ++ * qbman_swp_init() - Create a functional object representing the given ++ * QBMan portal descriptor. ++ * @d: the given qbman swp descriptor ++ * ++ * Return qbman_swp portal object for success, NULL if the object cannot ++ * be created. ++ */ ++struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d); ++/** ++ * qbman_swp_finish() - Create and destroy a functional object representing ++ * the given QBMan portal descriptor. ++ * @p: the qbman_swp object to be destroyed. ++ * ++ */ ++void qbman_swp_finish(struct qbman_swp *p); ++ ++/** ++ * qbman_swp_get_desc() - Get the descriptor of the given portal object. ++ * @p: the given portal object. ++ * ++ * Return the descriptor for this portal. ++ */ ++const struct qbman_swp_desc *qbman_swp_get_desc(struct qbman_swp *p); ++ ++ /**************/ ++ /* Interrupts */ ++ /**************/ ++ ++/* See the QBMan driver API documentation for details on the interrupt ++ * mechanisms. */ ++#define QBMAN_SWP_INTERRUPT_EQRI ((uint32_t)0x00000001) ++#define QBMAN_SWP_INTERRUPT_EQDI ((uint32_t)0x00000002) ++#define QBMAN_SWP_INTERRUPT_DQRI ((uint32_t)0x00000004) ++#define QBMAN_SWP_INTERRUPT_RCRI ((uint32_t)0x00000008) ++#define QBMAN_SWP_INTERRUPT_RCDI ((uint32_t)0x00000010) ++#define QBMAN_SWP_INTERRUPT_VDCI ((uint32_t)0x00000020) ++ ++/** ++ * qbman_swp_interrupt_get_vanish() ++ * qbman_swp_interrupt_set_vanish() - Get/Set the data in software portal ++ * interrupt status disable register. ++ * @p: the given software portal object. ++ * @mask: The mask to set in SWP_IDSR register. ++ * ++ * Return the settings in SWP_ISDR register for Get function. ++ */ ++uint32_t qbman_swp_interrupt_get_vanish(struct qbman_swp *p); ++void qbman_swp_interrupt_set_vanish(struct qbman_swp *p, uint32_t mask); ++ ++/** ++ * qbman_swp_interrupt_read_status() ++ * qbman_swp_interrupt_clear_status() - Get/Set the data in software portal ++ * interrupt status register. ++ * @p: the given software portal object. ++ * @mask: The mask to set in SWP_ISR register. ++ * ++ * Return the settings in SWP_ISR register for Get function. ++ * ++ */ ++uint32_t qbman_swp_interrupt_read_status(struct qbman_swp *p); ++void qbman_swp_interrupt_clear_status(struct qbman_swp *p, uint32_t mask); ++ ++/** ++ * qbman_swp_interrupt_get_trigger() ++ * qbman_swp_interrupt_set_trigger() - Get/Set the data in software portal ++ * interrupt enable register. ++ * @p: the given software portal object. ++ * @mask: The mask to set in SWP_IER register. ++ * ++ * Return the settings in SWP_IER register for Get function. ++ */ ++uint32_t qbman_swp_interrupt_get_trigger(struct qbman_swp *p); ++void qbman_swp_interrupt_set_trigger(struct qbman_swp *p, uint32_t mask); ++ ++/** ++ * qbman_swp_interrupt_get_inhibit() ++ * qbman_swp_interrupt_set_inhibit() - Set/Set the data in software portal ++ * interrupt inhibit register. ++ * @p: the given software portal object. ++ * @mask: The mask to set in SWP_IIR register. ++ * ++ * Return the settings in SWP_IIR register for Get function. ++ */ ++int qbman_swp_interrupt_get_inhibit(struct qbman_swp *p); ++void qbman_swp_interrupt_set_inhibit(struct qbman_swp *p, int inhibit); ++ ++ /************/ ++ /* Dequeues */ ++ /************/ ++ ++/* See the QBMan driver API documentation for details on the enqueue ++ * mechanisms. NB: the use of a 'dpaa2_' prefix for this type is because it is ++ * primarily used by the "DPIO" layer that sits above (and hides) the QBMan ++ * driver. The structure is defined in the DPIO interface, but to avoid circular ++ * dependencies we just pre/re-declare it here opaquely. */ ++struct dpaa2_dq; ++ ++/* ------------------- */ ++/* Push-mode dequeuing */ ++/* ------------------- */ ++ ++/** ++ * qbman_swp_push_get() - Get the push dequeue setup. ++ * @p: the software portal object. ++ * @channel_idx: the channel index to query. ++ * @enabled: returned boolean to show whether the push dequeue is enabled for ++ * the given channel. ++ */ ++void qbman_swp_push_get(struct qbman_swp *, uint8_t channel_idx, int *enabled); ++/** ++ * qbman_swp_push_set() - Enable or disable push dequeue. ++ * @p: the software portal object. ++ * @channel_idx: the channel index.. ++ * @enable: enable or disable push dequeue. ++ * ++ * The user of a portal can enable and disable push-mode dequeuing of up to 16 ++ * channels independently. It does not specify this toggling by channel IDs, but ++ * rather by specifying the index (from 0 to 15) that has been mapped to the ++ * desired channel. ++ */ ++void qbman_swp_push_set(struct qbman_swp *, uint8_t channel_idx, int enable); ++ ++/* ------------------- */ ++/* Pull-mode dequeuing */ ++/* ------------------- */ ++ ++/** ++ * struct qbman_pull_desc - the structure for pull dequeue descriptor ++ */ ++struct qbman_pull_desc { ++ uint32_t dont_manipulate_directly[6]; ++}; ++ ++enum qbman_pull_type_e { ++ /* dequeue with priority precedence, respect intra-class scheduling */ ++ qbman_pull_type_prio = 1, ++ /* dequeue with active FQ precedence, respect ICS */ ++ qbman_pull_type_active, ++ /* dequeue with active FQ precedence, no ICS */ ++ qbman_pull_type_active_noics ++}; ++ ++/** ++ * qbman_pull_desc_clear() - Clear the contents of a descriptor to ++ * default/starting state. ++ * @d: the pull dequeue descriptor to be cleared. ++ */ ++void qbman_pull_desc_clear(struct qbman_pull_desc *d); ++ ++/** ++ * qbman_pull_desc_set_storage()- Set the pull dequeue storage ++ * @d: the pull dequeue descriptor to be set. ++ * @storage: the pointer of the memory to store the dequeue result. ++ * @storage_phys: the physical address of the storage memory. ++ * @stash: to indicate whether write allocate is enabled. ++ * ++ * If not called, or if called with 'storage' as NULL, the result pull dequeues ++ * will produce results to DQRR. If 'storage' is non-NULL, then results are ++ * produced to the given memory location (using the physical/DMA address which ++ * the caller provides in 'storage_phys'), and 'stash' controls whether or not ++ * those writes to main-memory express a cache-warming attribute. ++ */ ++void qbman_pull_desc_set_storage(struct qbman_pull_desc *d, ++ struct dpaa2_dq *storage, ++ dma_addr_t storage_phys, ++ int stash); ++/** ++ * qbman_pull_desc_set_numframes() - Set the number of frames to be dequeued. ++ * @d: the pull dequeue descriptor to be set. ++ * @numframes: number of frames to be set, must be between 1 and 16, inclusive. ++ */ ++void qbman_pull_desc_set_numframes(struct qbman_pull_desc *, uint8_t numframes); ++ ++/** ++ * qbman_pull_desc_set_fq() - Set fqid from which the dequeue command dequeues. ++ * @fqid: the frame queue index of the given FQ. ++ * ++ * qbman_pull_desc_set_wq() - Set wqid from which the dequeue command dequeues. ++ * @wqid: composed of channel id and wqid within the channel. ++ * @dct: the dequeue command type. ++ * ++ * qbman_pull_desc_set_channel() - Set channelid from which the dequeue command ++ * dequeues. ++ * @chid: the channel id to be dequeued. ++ * @dct: the dequeue command type. ++ * ++ * Exactly one of the following descriptor "actions" should be set. (Calling any ++ * one of these will replace the effect of any prior call to one of these.) ++ * - pull dequeue from the given frame queue (FQ) ++ * - pull dequeue from any FQ in the given work queue (WQ) ++ * - pull dequeue from any FQ in any WQ in the given channel ++ */ ++void qbman_pull_desc_set_fq(struct qbman_pull_desc *, uint32_t fqid); ++void qbman_pull_desc_set_wq(struct qbman_pull_desc *, uint32_t wqid, ++ enum qbman_pull_type_e dct); ++void qbman_pull_desc_set_channel(struct qbman_pull_desc *, uint32_t chid, ++ enum qbman_pull_type_e dct); ++ ++/** ++ * qbman_swp_pull() - Issue the pull dequeue command ++ * @s: the software portal object. ++ * @d: the software portal descriptor which has been configured with ++ * the set of qbman_pull_desc_set_*() calls. ++ * ++ * Return 0 for success, and -EBUSY if the software portal is not ready ++ * to do pull dequeue. ++ */ ++int qbman_swp_pull(struct qbman_swp *, struct qbman_pull_desc *d); ++ ++/* -------------------------------- */ ++/* Polling DQRR for dequeue results */ ++/* -------------------------------- */ ++ ++/** ++ * qbman_swp_dqrr_next() - Get an valid DQRR entry. ++ * @s: the software portal object. ++ * ++ * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry ++ * only once, so repeated calls can return a sequence of DQRR entries, without ++ * requiring they be consumed immediately or in any particular order. ++ */ ++const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s); ++ ++/** ++ * qbman_swp_dqrr_consume() - Consume DQRR entries previously returned from ++ * qbman_swp_dqrr_next(). ++ * @s: the software portal object. ++ * @dq: the DQRR entry to be consumed. ++ */ ++void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct dpaa2_dq *dq); ++ ++/* ------------------------------------------------- */ ++/* Polling user-provided storage for dequeue results */ ++/* ------------------------------------------------- */ ++/** ++ * qbman_result_has_new_result() - Check and get the dequeue response from the ++ * dq storage memory set in pull dequeue command ++ * @s: the software portal object. ++ * @dq: the dequeue result read from the memory. ++ * ++ * Only used for user-provided storage of dequeue results, not DQRR. For ++ * efficiency purposes, the driver will perform any required endianness ++ * conversion to ensure that the user's dequeue result storage is in host-endian ++ * format (whether or not that is the same as the little-endian format that ++ * hardware DMA'd to the user's storage). As such, once the user has called ++ * qbman_result_has_new_result() and been returned a valid dequeue result, ++ * they should not call it again on the same memory location (except of course ++ * if another dequeue command has been executed to produce a new result to that ++ * location). ++ * ++ * Return 1 for getting a valid dequeue result, or 0 for not getting a valid ++ * dequeue result. ++ */ ++int qbman_result_has_new_result(struct qbman_swp *, ++ const struct dpaa2_dq *); ++ ++/* -------------------------------------------------------- */ ++/* Parsing dequeue entries (DQRR and user-provided storage) */ ++/* -------------------------------------------------------- */ ++ ++/** ++ * qbman_result_is_DQ() - check the dequeue result is a dequeue response or not ++ * @dq: the dequeue result to be checked. ++ * ++ * DQRR entries may contain non-dequeue results, ie. notifications ++ */ ++int qbman_result_is_DQ(const struct dpaa2_dq *); ++ ++/** ++ * qbman_result_is_SCN() - Check the dequeue result is notification or not ++ * @dq: the dequeue result to be checked. ++ * ++ * All the non-dequeue results (FQDAN/CDAN/CSCN/...) are "state change ++ * notifications" of one type or another. Some APIs apply to all of them, of the ++ * form qbman_result_SCN_***(). ++ */ ++static inline int qbman_result_is_SCN(const struct dpaa2_dq *dq) ++{ ++ return !qbman_result_is_DQ(dq); ++} ++ ++/** ++ * Recognise different notification types, only required if the user allows for ++ * these to occur, and cares about them when they do. ++ */ ++int qbman_result_is_FQDAN(const struct dpaa2_dq *); ++ /* FQ Data Availability */ ++int qbman_result_is_CDAN(const struct dpaa2_dq *); ++ /* Channel Data Availability */ ++int qbman_result_is_CSCN(const struct dpaa2_dq *); ++ /* Congestion State Change */ ++int qbman_result_is_BPSCN(const struct dpaa2_dq *); ++ /* Buffer Pool State Change */ ++int qbman_result_is_CGCU(const struct dpaa2_dq *); ++ /* Congestion Group Count Update */ ++/* Frame queue state change notifications; (FQDAN in theory counts too as it ++ * leaves a FQ parked, but it is primarily a data availability notification) */ ++int qbman_result_is_FQRN(const struct dpaa2_dq *); /* Retirement */ ++int qbman_result_is_FQRNI(const struct dpaa2_dq *); ++ /* Retirement Immediate */ ++int qbman_result_is_FQPN(const struct dpaa2_dq *); /* Park */ ++ ++/* NB: for parsing dequeue results (when "is_DQ" is TRUE), use the higher-layer ++ * dpaa2_dq_*() functions. */ ++ ++/* State-change notifications (FQDAN/CDAN/CSCN/...). */ ++/** ++ * qbman_result_SCN_state() - Get the state field in State-change notification ++ */ ++uint8_t qbman_result_SCN_state(const struct dpaa2_dq *); ++/** ++ * qbman_result_SCN_rid() - Get the resource id in State-change notification ++ */ ++uint32_t qbman_result_SCN_rid(const struct dpaa2_dq *); ++/** ++ * qbman_result_SCN_ctx() - Get the context data in State-change notification ++ */ ++uint64_t qbman_result_SCN_ctx(const struct dpaa2_dq *); ++/** ++ * qbman_result_SCN_state_in_mem() - Get the state field in State-change ++ * notification which is written to memory instead of DQRR. ++ */ ++uint8_t qbman_result_SCN_state_in_mem(const struct dpaa2_dq *); ++/** ++ * qbman_result_SCN_rid_in_mem() - Get the resource id in State-change ++ * notification which is written to memory instead of DQRR. ++ */ ++uint32_t qbman_result_SCN_rid_in_mem(const struct dpaa2_dq *); ++ ++/* Type-specific "resource IDs". Mainly for illustration purposes, though it ++ * also gives the appropriate type widths. */ ++#define qbman_result_FQDAN_fqid(dq) qbman_result_SCN_rid(dq) ++#define qbman_result_FQRN_fqid(dq) qbman_result_SCN_rid(dq) ++#define qbman_result_FQRNI_fqid(dq) qbman_result_SCN_rid(dq) ++#define qbman_result_FQPN_fqid(dq) qbman_result_SCN_rid(dq) ++#define qbman_result_CDAN_cid(dq) ((uint16_t)qbman_result_SCN_rid(dq)) ++#define qbman_result_CSCN_cgid(dq) ((uint16_t)qbman_result_SCN_rid(dq)) ++ ++/** ++ * qbman_result_bpscn_bpid() - Get the bpid from BPSCN ++ * ++ * Return the buffer pool id. ++ */ ++uint16_t qbman_result_bpscn_bpid(const struct dpaa2_dq *); ++/** ++ * qbman_result_bpscn_has_free_bufs() - Check whether there are free ++ * buffers in the pool from BPSCN. ++ * ++ * Return the number of free buffers. ++ */ ++int qbman_result_bpscn_has_free_bufs(const struct dpaa2_dq *); ++/** ++ * qbman_result_bpscn_is_depleted() - Check BPSCN to see whether the ++ * buffer pool is depleted. ++ * ++ * Return the status of buffer pool depletion. ++ */ ++int qbman_result_bpscn_is_depleted(const struct dpaa2_dq *); ++/** ++ * qbman_result_bpscn_is_surplus() - Check BPSCN to see whether the buffer ++ * pool is surplus or not. ++ * ++ * Return the status of buffer pool surplus. ++ */ ++int qbman_result_bpscn_is_surplus(const struct dpaa2_dq *); ++/** ++ * qbman_result_bpscn_ctx() - Get the BPSCN CTX from BPSCN message ++ * ++ * Return the BPSCN context. ++ */ ++uint64_t qbman_result_bpscn_ctx(const struct dpaa2_dq *); ++ ++/* Parsing CGCU */ ++/** ++ * qbman_result_cgcu_cgid() - Check CGCU resouce id, i.e. cgid ++ * ++ * Return the CGCU resource id. ++ */ ++uint16_t qbman_result_cgcu_cgid(const struct dpaa2_dq *); ++/** ++ * qbman_result_cgcu_icnt() - Get the I_CNT from CGCU ++ * ++ * Return instantaneous count in the CGCU notification. ++ */ ++uint64_t qbman_result_cgcu_icnt(const struct dpaa2_dq *); ++ ++ /************/ ++ /* Enqueues */ ++ /************/ ++/** ++ * struct qbman_eq_desc - structure of enqueue descriptor ++ */ ++struct qbman_eq_desc { ++ uint32_t dont_manipulate_directly[8]; ++}; ++ ++/** ++ * struct qbman_eq_response - structure of enqueue response ++ */ ++struct qbman_eq_response { ++ uint32_t dont_manipulate_directly[16]; ++}; ++ ++/** ++ * qbman_eq_desc_clear() - Clear the contents of a descriptor to ++ * default/starting state. ++ */ ++void qbman_eq_desc_clear(struct qbman_eq_desc *); ++ ++/* Exactly one of the following descriptor "actions" should be set. (Calling ++ * any one of these will replace the effect of any prior call to one of these.) ++ * - enqueue without order-restoration ++ * - enqueue with order-restoration ++ * - fill a hole in the order-restoration sequence, without any enqueue ++ * - advance NESN (Next Expected Sequence Number), without any enqueue ++ * 'respond_success' indicates whether an enqueue response should be DMA'd ++ * after success (otherwise a response is DMA'd only after failure). ++ * 'incomplete' indicates that other fragments of the same 'seqnum' are yet to ++ * be enqueued. ++ */ ++/** ++ * qbman_eq_desc_set_no_orp() - Set enqueue descriptor without orp ++ * @d: the enqueue descriptor. ++ * @response_success: 1 = enqueue with response always; 0 = enqueue with ++ * rejections returned on a FQ. ++ */ ++void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success); ++ ++/** ++ * qbman_eq_desc_set_orp() - Set order-resotration in the enqueue descriptor ++ * @d: the enqueue descriptor. ++ * @response_success: 1 = enqueue with response always; 0 = enqueue with ++ * rejections returned on a FQ. ++ * @opr_id: the order point record id. ++ * @seqnum: the order restoration sequence number. ++ * @incomplete: indiates whether this is the last fragments using the same ++ * sequeue number. ++ */ ++void qbman_eq_desc_set_orp(struct qbman_eq_desc *d, int respond_success, ++ uint32_t opr_id, uint32_t seqnum, int incomplete); ++ ++/** ++ * qbman_eq_desc_set_orp_hole() - fill a hole in the order-restoration sequence ++ * without any enqueue ++ * @d: the enqueue descriptor. ++ * @opr_id: the order point record id. ++ * @seqnum: the order restoration sequence number. ++ */ ++void qbman_eq_desc_set_orp_hole(struct qbman_eq_desc *d, uint32_t opr_id, ++ uint32_t seqnum); ++ ++/** ++ * qbman_eq_desc_set_orp_nesn() - advance NESN (Next Expected Sequence Number) ++ * without any enqueue ++ * @d: the enqueue descriptor. ++ * @opr_id: the order point record id. ++ * @seqnum: the order restoration sequence number. ++ */ ++void qbman_eq_desc_set_orp_nesn(struct qbman_eq_desc *d, uint32_t opr_id, ++ uint32_t seqnum); ++ ++/** ++ * qbman_eq_desc_set_response() - Set the enqueue response info. ++ * @d: the enqueue descriptor ++ * @storage_phys: the physical address of the enqueue response in memory. ++ * @stash: indicate that the write allocation enabled or not. ++ * ++ * In the case where an enqueue response is DMA'd, this determines where that ++ * response should go. (The physical/DMA address is given for hardware's ++ * benefit, but software should interpret it as a "struct qbman_eq_response" ++ * data structure.) 'stash' controls whether or not the write to main-memory ++ * expresses a cache-warming attribute. ++ */ ++void qbman_eq_desc_set_response(struct qbman_eq_desc *d, ++ dma_addr_t storage_phys, ++ int stash); ++/** ++ * qbman_eq_desc_set_token() - Set token for the enqueue command ++ * @d: the enqueue descriptor ++ * @token: the token to be set. ++ * ++ * token is the value that shows up in an enqueue response that can be used to ++ * detect when the results have been published. The easiest technique is to zero ++ * result "storage" before issuing an enqueue, and use any non-zero 'token' ++ * value. ++ */ ++void qbman_eq_desc_set_token(struct qbman_eq_desc *d, uint8_t token); ++ ++/** ++ * qbman_eq_desc_set_fq() ++ * qbman_eq_desc_set_qd() - Set eithe FQ or Queuing Destination for the enqueue ++ * command. ++ * @d: the enqueue descriptor ++ * @fqid: the id of the frame queue to be enqueued. ++ * @qdid: the id of the queuing destination to be enqueued. ++ * @qd_bin: the queuing destination bin ++ * @qd_prio: the queuing destination priority. ++ * ++ * Exactly one of the following descriptor "targets" should be set. (Calling any ++ * one of these will replace the effect of any prior call to one of these.) ++ * - enqueue to a frame queue ++ * - enqueue to a queuing destination ++ * Note, that none of these will have any affect if the "action" type has been ++ * set to "orp_hole" or "orp_nesn". ++ */ ++void qbman_eq_desc_set_fq(struct qbman_eq_desc *, uint32_t fqid); ++void qbman_eq_desc_set_qd(struct qbman_eq_desc *, uint32_t qdid, ++ uint32_t qd_bin, uint32_t qd_prio); ++ ++/** ++ * qbman_eq_desc_set_eqdi() - enable/disable EQDI interrupt ++ * @d: the enqueue descriptor ++ * @enable: boolean to enable/disable EQDI ++ * ++ * Determines whether or not the portal's EQDI interrupt source should be ++ * asserted after the enqueue command is completed. ++ */ ++void qbman_eq_desc_set_eqdi(struct qbman_eq_desc *, int enable); ++ ++/** ++ * qbman_eq_desc_set_dca() - Set DCA mode in the enqueue command. ++ * @d: the enqueue descriptor. ++ * @enable: enabled/disable DCA mode. ++ * @dqrr_idx: DCAP_CI, the DCAP consumer index. ++ * @park: determine the whether park the FQ or not ++ * ++ * Determines whether or not a portal DQRR entry should be consumed once the ++ * enqueue command is completed. (And if so, and the DQRR entry corresponds ++ * to a held-active (order-preserving) FQ, whether the FQ should be parked ++ * instead of being rescheduled.) ++ */ ++void qbman_eq_desc_set_dca(struct qbman_eq_desc *, int enable, ++ uint32_t dqrr_idx, int park); ++ ++/** ++ * qbman_swp_enqueue() - Issue an enqueue command. ++ * @s: the software portal used for enqueue. ++ * @d: the enqueue descriptor. ++ * @fd: the frame descriptor to be enqueued. ++ * ++ * Please note that 'fd' should only be NULL if the "action" of the ++ * descriptor is "orp_hole" or "orp_nesn". ++ * ++ * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready. ++ */ ++int qbman_swp_enqueue(struct qbman_swp *, const struct qbman_eq_desc *, ++ const struct qbman_fd *fd); ++ ++/** ++ * qbman_swp_enqueue_thresh() - Set the threshold for EQRI interrupt. ++ * ++ * An EQRI interrupt can be generated when the fill-level of EQCR falls below ++ * the 'thresh' value set here. Setting thresh==0 (the default) disables. ++ */ ++int qbman_swp_enqueue_thresh(struct qbman_swp *, unsigned int thresh); ++ ++ /*******************/ ++ /* Buffer releases */ ++ /*******************/ ++/** ++ * struct qbman_release_desc - The structure for buffer release descriptor ++ */ ++struct qbman_release_desc { ++ uint32_t dont_manipulate_directly[1]; ++}; ++ ++/** ++ * qbman_release_desc_clear() - Clear the contents of a descriptor to ++ * default/starting state. ++ */ ++void qbman_release_desc_clear(struct qbman_release_desc *); ++ ++/** ++ * qbman_release_desc_set_bpid() - Set the ID of the buffer pool to release to ++ */ ++void qbman_release_desc_set_bpid(struct qbman_release_desc *, uint32_t bpid); ++ ++/** ++ * qbman_release_desc_set_rcdi() - Determines whether or not the portal's RCDI ++ * interrupt source should be asserted after the release command is completed. ++ */ ++void qbman_release_desc_set_rcdi(struct qbman_release_desc *, int enable); ++ ++/** ++ * qbman_swp_release() - Issue a buffer release command. ++ * @s: the software portal object. ++ * @d: the release descriptor. ++ * @buffers: a pointer pointing to the buffer address to be released. ++ * @num_buffers: number of buffers to be released, must be less than 8. ++ * ++ * Return 0 for success, -EBUSY if the release command ring is not ready. ++ */ ++int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, ++ const uint64_t *buffers, unsigned int num_buffers); ++ ++ /*******************/ ++ /* Buffer acquires */ ++ /*******************/ ++ ++/** ++ * qbman_swp_acquire() - Issue a buffer acquire command. ++ * @s: the software portal object. ++ * @bpid: the buffer pool index. ++ * @buffers: a pointer pointing to the acquired buffer address|es. ++ * @num_buffers: number of buffers to be acquired, must be less than 8. ++ * ++ * Return 0 for success, or negative error code if the acquire command ++ * fails. ++ */ ++int qbman_swp_acquire(struct qbman_swp *, uint32_t bpid, uint64_t *buffers, ++ unsigned int num_buffers); ++ ++ /*****************/ ++ /* FQ management */ ++ /*****************/ ++ ++/** ++ * qbman_swp_fq_schedule() - Move the fq to the scheduled state. ++ * @s: the software portal object. ++ * @fqid: the index of frame queue to be scheduled. ++ * ++ * There are a couple of different ways that a FQ can end up parked state, ++ * This schedules it. ++ * ++ * Return 0 for success, or negative error code for failure. ++ */ ++int qbman_swp_fq_schedule(struct qbman_swp *s, uint32_t fqid); ++ ++/** ++ * qbman_swp_fq_force() - Force the FQ to fully scheduled state. ++ * @s: the software portal object. ++ * @fqid: the index of frame queue to be forced. ++ * ++ * Force eligible will force a tentatively-scheduled FQ to be fully-scheduled ++ * and thus be available for selection by any channel-dequeuing behaviour (push ++ * or pull). If the FQ is subsequently "dequeued" from the channel and is still ++ * empty at the time this happens, the resulting dq_entry will have no FD. ++ * (qbman_result_DQ_fd() will return NULL.) ++ * ++ * Return 0 for success, or negative error code for failure. ++ */ ++int qbman_swp_fq_force(struct qbman_swp *s, uint32_t fqid); ++ ++/** ++ * qbman_swp_fq_xon() ++ * qbman_swp_fq_xoff() - XON/XOFF the frame queue. ++ * @s: the software portal object. ++ * @fqid: the index of frame queue. ++ * ++ * These functions change the FQ flow-control stuff between XON/XOFF. (The ++ * default is XON.) This setting doesn't affect enqueues to the FQ, just ++ * dequeues. XOFF FQs will remain in the tenatively-scheduled state, even when ++ * non-empty, meaning they won't be selected for scheduled dequeuing. If a FQ is ++ * changed to XOFF after it had already become truly-scheduled to a channel, and ++ * a pull dequeue of that channel occurs that selects that FQ for dequeuing, ++ * then the resulting dq_entry will have no FD. (qbman_result_DQ_fd() will ++ * return NULL.) ++ * ++ * Return 0 for success, or negative error code for failure. ++ */ ++int qbman_swp_fq_xon(struct qbman_swp *s, uint32_t fqid); ++int qbman_swp_fq_xoff(struct qbman_swp *s, uint32_t fqid); ++ ++ /**********************/ ++ /* Channel management */ ++ /**********************/ ++ ++/* If the user has been allocated a channel object that is going to generate ++ * CDANs to another channel, then these functions will be necessary. ++ * CDAN-enabled channels only generate a single CDAN notification, after which ++ * it they need to be reenabled before they'll generate another. (The idea is ++ * that pull dequeuing will occur in reaction to the CDAN, followed by a ++ * reenable step.) Each function generates a distinct command to hardware, so a ++ * combination function is provided if the user wishes to modify the "context" ++ * (which shows up in each CDAN message) each time they reenable, as a single ++ * command to hardware. */ ++/** ++ * qbman_swp_CDAN_set_context() - Set CDAN context ++ * @s: the software portal object. ++ * @channelid: the channel index. ++ * @ctx: the context to be set in CDAN. ++ * ++ * Return 0 for success, or negative error code for failure. ++ */ ++int qbman_swp_CDAN_set_context(struct qbman_swp *, uint16_t channelid, ++ uint64_t ctx); ++ ++/** ++ * qbman_swp_CDAN_enable() - Enable CDAN for the channel. ++ * @s: the software portal object. ++ * @channelid: the index of the channel to generate CDAN. ++ * ++ * Return 0 for success, or negative error code for failure. ++ */ ++int qbman_swp_CDAN_enable(struct qbman_swp *, uint16_t channelid); ++ ++/** ++ * qbman_swp_CDAN_disable() - disable CDAN for the channel. ++ * @s: the software portal object. ++ * @channelid: the index of the channel to generate CDAN. ++ * ++ * Return 0 for success, or negative error code for failure. ++ */ ++int qbman_swp_CDAN_disable(struct qbman_swp *, uint16_t channelid); ++ ++/** ++ * qbman_swp_CDAN_set_context_enable() - Set CDAN contest and enable CDAN ++ * @s: the software portal object. ++ * @channelid: the index of the channel to generate CDAN. ++ * @ctx: the context set in CDAN. ++ * ++ * Return 0 for success, or negative error code for failure. ++ */ ++int qbman_swp_CDAN_set_context_enable(struct qbman_swp *, uint16_t channelid, ++ uint64_t ctx); ++ ++#endif /* !_FSL_QBMAN_PORTAL_H */ +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.c +@@ -0,0 +1,846 @@ ++/* Copyright (C) 2015 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 "qbman_portal.h" ++#include "qbman_debug.h" ++#include "fsl_qbman_portal.h" ++ ++/* QBMan portal management command code */ ++#define QBMAN_BP_QUERY 0x32 ++#define QBMAN_FQ_QUERY 0x44 ++#define QBMAN_FQ_QUERY_NP 0x45 ++#define QBMAN_CGR_QUERY 0x51 ++#define QBMAN_WRED_QUERY 0x54 ++#define QBMAN_CGR_STAT_QUERY 0x55 ++#define QBMAN_CGR_STAT_QUERY_CLR 0x56 ++ ++enum qbman_attr_usage_e { ++ qbman_attr_usage_fq, ++ qbman_attr_usage_bpool, ++ qbman_attr_usage_cgr, ++}; ++ ++struct int_qbman_attr { ++ uint32_t words[32]; ++ enum qbman_attr_usage_e usage; ++}; ++ ++#define attr_type_set(a, e) \ ++{ \ ++ struct qbman_attr *__attr = a; \ ++ enum qbman_attr_usage_e __usage = e; \ ++ ((struct int_qbman_attr *)__attr)->usage = __usage; \ ++} ++ ++#define ATTR32(d) (&(d)->dont_manipulate_directly[0]) ++#define ATTR32_1(d) (&(d)->dont_manipulate_directly[16]) ++ ++static struct qb_attr_code code_bp_bpid = QB_CODE(0, 16, 16); ++static struct qb_attr_code code_bp_bdi = QB_CODE(1, 16, 1); ++static struct qb_attr_code code_bp_va = QB_CODE(1, 17, 1); ++static struct qb_attr_code code_bp_wae = QB_CODE(1, 18, 1); ++static struct qb_attr_code code_bp_swdet = QB_CODE(4, 0, 16); ++static struct qb_attr_code code_bp_swdxt = QB_CODE(4, 16, 16); ++static struct qb_attr_code code_bp_hwdet = QB_CODE(5, 0, 16); ++static struct qb_attr_code code_bp_hwdxt = QB_CODE(5, 16, 16); ++static struct qb_attr_code code_bp_swset = QB_CODE(6, 0, 16); ++static struct qb_attr_code code_bp_swsxt = QB_CODE(6, 16, 16); ++static struct qb_attr_code code_bp_vbpid = QB_CODE(7, 0, 14); ++static struct qb_attr_code code_bp_icid = QB_CODE(7, 16, 15); ++static struct qb_attr_code code_bp_pl = QB_CODE(7, 31, 1); ++static struct qb_attr_code code_bp_bpscn_addr_lo = QB_CODE(8, 0, 32); ++static struct qb_attr_code code_bp_bpscn_addr_hi = QB_CODE(9, 0, 32); ++static struct qb_attr_code code_bp_bpscn_ctx_lo = QB_CODE(10, 0, 32); ++static struct qb_attr_code code_bp_bpscn_ctx_hi = QB_CODE(11, 0, 32); ++static struct qb_attr_code code_bp_hw_targ = QB_CODE(12, 0, 16); ++static struct qb_attr_code code_bp_state = QB_CODE(1, 24, 3); ++static struct qb_attr_code code_bp_fill = QB_CODE(2, 0, 32); ++static struct qb_attr_code code_bp_hdptr = QB_CODE(3, 0, 32); ++static struct qb_attr_code code_bp_sdcnt = QB_CODE(13, 0, 8); ++static struct qb_attr_code code_bp_hdcnt = QB_CODE(13, 1, 8); ++static struct qb_attr_code code_bp_sscnt = QB_CODE(13, 2, 8); ++ ++void qbman_bp_attr_clear(struct qbman_attr *a) ++{ ++ memset(a, 0, sizeof(*a)); ++ attr_type_set(a, qbman_attr_usage_bpool); ++} ++ ++int qbman_bp_query(struct qbman_swp *s, uint32_t bpid, ++ struct qbman_attr *a) ++{ ++ uint32_t *p; ++ uint32_t verb, rslt; ++ uint32_t *attr = ATTR32(a); ++ ++ qbman_bp_attr_clear(a); ++ ++ /* Start the management command */ ++ p = qbman_swp_mc_start(s); ++ if (!p) ++ return -EBUSY; ++ ++ /* Encode the caller-provided attributes */ ++ qb_attr_code_encode(&code_bp_bpid, p, bpid); ++ ++ /* Complete the management command */ ++ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_BP_QUERY); ++ ++ /* Decode the outcome */ ++ verb = qb_attr_code_decode(&code_generic_verb, p); ++ rslt = qb_attr_code_decode(&code_generic_rslt, p); ++ BUG_ON(verb != QBMAN_BP_QUERY); ++ ++ /* Determine success or failure */ ++ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { ++ pr_err("Query of BPID 0x%x failed, code=0x%02x\n", bpid, rslt); ++ return -EIO; ++ } ++ ++ /* For the query, word[0] of the result contains only the ++ * verb/rslt fields, so skip word[0]. ++ */ ++ word_copy(&attr[1], &p[1], 15); ++ return 0; ++} ++ ++void qbman_bp_attr_get_bdi(struct qbman_attr *a, int *bdi, int *va, int *wae) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *bdi = !!qb_attr_code_decode(&code_bp_bdi, p); ++ *va = !!qb_attr_code_decode(&code_bp_va, p); ++ *wae = !!qb_attr_code_decode(&code_bp_wae, p); ++} ++ ++static uint32_t qbman_bp_thresh_to_value(uint32_t val) ++{ ++ return (val & 0xff) << ((val & 0xf00) >> 8); ++} ++ ++void qbman_bp_attr_get_swdet(struct qbman_attr *a, uint32_t *swdet) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *swdet = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swdet, ++ p)); ++} ++void qbman_bp_attr_get_swdxt(struct qbman_attr *a, uint32_t *swdxt) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *swdxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swdxt, ++ p)); ++} ++void qbman_bp_attr_get_hwdet(struct qbman_attr *a, uint32_t *hwdet) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *hwdet = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_hwdet, ++ p)); ++} ++void qbman_bp_attr_get_hwdxt(struct qbman_attr *a, uint32_t *hwdxt) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *hwdxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_hwdxt, ++ p)); ++} ++ ++void qbman_bp_attr_get_swset(struct qbman_attr *a, uint32_t *swset) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *swset = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swset, ++ p)); ++} ++ ++void qbman_bp_attr_get_swsxt(struct qbman_attr *a, uint32_t *swsxt) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *swsxt = qbman_bp_thresh_to_value(qb_attr_code_decode(&code_bp_swsxt, ++ p)); ++} ++ ++void qbman_bp_attr_get_vbpid(struct qbman_attr *a, uint32_t *vbpid) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *vbpid = qb_attr_code_decode(&code_bp_vbpid, p); ++} ++ ++void qbman_bp_attr_get_icid(struct qbman_attr *a, uint32_t *icid, int *pl) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *icid = qb_attr_code_decode(&code_bp_icid, p); ++ *pl = !!qb_attr_code_decode(&code_bp_pl, p); ++} ++ ++void qbman_bp_attr_get_bpscn_addr(struct qbman_attr *a, uint64_t *bpscn_addr) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *bpscn_addr = ((uint64_t)qb_attr_code_decode(&code_bp_bpscn_addr_hi, ++ p) << 32) | ++ (uint64_t)qb_attr_code_decode(&code_bp_bpscn_addr_lo, ++ p); ++} ++ ++void qbman_bp_attr_get_bpscn_ctx(struct qbman_attr *a, uint64_t *bpscn_ctx) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *bpscn_ctx = ((uint64_t)qb_attr_code_decode(&code_bp_bpscn_ctx_hi, p) ++ << 32) | ++ (uint64_t)qb_attr_code_decode(&code_bp_bpscn_ctx_lo, ++ p); ++} ++ ++void qbman_bp_attr_get_hw_targ(struct qbman_attr *a, uint32_t *hw_targ) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ *hw_targ = qb_attr_code_decode(&code_bp_hw_targ, p); ++} ++ ++int qbman_bp_info_has_free_bufs(struct qbman_attr *a) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ return !(int)(qb_attr_code_decode(&code_bp_state, p) & 0x1); ++} ++ ++int qbman_bp_info_is_depleted(struct qbman_attr *a) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ return (int)(qb_attr_code_decode(&code_bp_state, p) & 0x2); ++} ++ ++int qbman_bp_info_is_surplus(struct qbman_attr *a) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ return (int)(qb_attr_code_decode(&code_bp_state, p) & 0x4); ++} ++ ++uint32_t qbman_bp_info_num_free_bufs(struct qbman_attr *a) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ return qb_attr_code_decode(&code_bp_fill, p); ++} ++ ++uint32_t qbman_bp_info_hdptr(struct qbman_attr *a) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ return qb_attr_code_decode(&code_bp_hdptr, p); ++} ++ ++uint32_t qbman_bp_info_sdcnt(struct qbman_attr *a) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ return qb_attr_code_decode(&code_bp_sdcnt, p); ++} ++ ++uint32_t qbman_bp_info_hdcnt(struct qbman_attr *a) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ return qb_attr_code_decode(&code_bp_hdcnt, p); ++} ++ ++uint32_t qbman_bp_info_sscnt(struct qbman_attr *a) ++{ ++ uint32_t *p = ATTR32(a); ++ ++ return qb_attr_code_decode(&code_bp_sscnt, p); ++} ++ ++static struct qb_attr_code code_fq_fqid = QB_CODE(1, 0, 24); ++static struct qb_attr_code code_fq_cgrid = QB_CODE(2, 16, 16); ++static struct qb_attr_code code_fq_destwq = QB_CODE(3, 0, 15); ++static struct qb_attr_code code_fq_fqctrl = QB_CODE(3, 24, 8); ++static struct qb_attr_code code_fq_icscred = QB_CODE(4, 0, 15); ++static struct qb_attr_code code_fq_tdthresh = QB_CODE(4, 16, 13); ++static struct qb_attr_code code_fq_oa_len = QB_CODE(5, 0, 12); ++static struct qb_attr_code code_fq_oa_ics = QB_CODE(5, 14, 1); ++static struct qb_attr_code code_fq_oa_cgr = QB_CODE(5, 15, 1); ++static struct qb_attr_code code_fq_mctl_bdi = QB_CODE(5, 24, 1); ++static struct qb_attr_code code_fq_mctl_ff = QB_CODE(5, 25, 1); ++static struct qb_attr_code code_fq_mctl_va = QB_CODE(5, 26, 1); ++static struct qb_attr_code code_fq_mctl_ps = QB_CODE(5, 27, 1); ++static struct qb_attr_code code_fq_ctx_lower32 = QB_CODE(6, 0, 32); ++static struct qb_attr_code code_fq_ctx_upper32 = QB_CODE(7, 0, 32); ++static struct qb_attr_code code_fq_icid = QB_CODE(8, 0, 15); ++static struct qb_attr_code code_fq_pl = QB_CODE(8, 15, 1); ++static struct qb_attr_code code_fq_vfqid = QB_CODE(9, 0, 24); ++static struct qb_attr_code code_fq_erfqid = QB_CODE(10, 0, 24); ++ ++void qbman_fq_attr_clear(struct qbman_attr *a) ++{ ++ memset(a, 0, sizeof(*a)); ++ attr_type_set(a, qbman_attr_usage_fq); ++} ++ ++/* FQ query function for programmable fields */ ++int qbman_fq_query(struct qbman_swp *s, uint32_t fqid, struct qbman_attr *desc) ++{ ++ uint32_t *p; ++ uint32_t verb, rslt; ++ uint32_t *d = ATTR32(desc); ++ ++ qbman_fq_attr_clear(desc); ++ ++ p = qbman_swp_mc_start(s); ++ if (!p) ++ return -EBUSY; ++ qb_attr_code_encode(&code_fq_fqid, p, fqid); ++ p = qbman_swp_mc_complete(s, p, QBMAN_FQ_QUERY); ++ ++ /* Decode the outcome */ ++ verb = qb_attr_code_decode(&code_generic_verb, p); ++ rslt = qb_attr_code_decode(&code_generic_rslt, p); ++ BUG_ON(verb != QBMAN_FQ_QUERY); ++ ++ /* Determine success or failure */ ++ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { ++ pr_err("Query of FQID 0x%x failed, code=0x%02x\n", ++ fqid, rslt); ++ return -EIO; ++ } ++ /* For the configure, word[0] of the command contains only the WE-mask. ++ * For the query, word[0] of the result contains only the verb/rslt ++ * fields. Skip word[0] in the latter case. */ ++ word_copy(&d[1], &p[1], 15); ++ return 0; ++} ++ ++void qbman_fq_attr_get_fqctrl(struct qbman_attr *d, uint32_t *fqctrl) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *fqctrl = qb_attr_code_decode(&code_fq_fqctrl, p); ++} ++ ++void qbman_fq_attr_get_cgrid(struct qbman_attr *d, uint32_t *cgrid) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *cgrid = qb_attr_code_decode(&code_fq_cgrid, p); ++} ++ ++void qbman_fq_attr_get_destwq(struct qbman_attr *d, uint32_t *destwq) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *destwq = qb_attr_code_decode(&code_fq_destwq, p); ++} ++ ++void qbman_fq_attr_get_icscred(struct qbman_attr *d, uint32_t *icscred) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *icscred = qb_attr_code_decode(&code_fq_icscred, p); ++} ++ ++static struct qb_attr_code code_tdthresh_exp = QB_CODE(0, 0, 5); ++static struct qb_attr_code code_tdthresh_mant = QB_CODE(0, 5, 8); ++static uint32_t qbman_thresh_to_value(uint32_t val) ++{ ++ uint32_t m, e; ++ ++ m = qb_attr_code_decode(&code_tdthresh_mant, &val); ++ e = qb_attr_code_decode(&code_tdthresh_exp, &val); ++ return m << e; ++} ++ ++void qbman_fq_attr_get_tdthresh(struct qbman_attr *d, uint32_t *tdthresh) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *tdthresh = qbman_thresh_to_value(qb_attr_code_decode(&code_fq_tdthresh, ++ p)); ++} ++ ++void qbman_fq_attr_get_oa(struct qbman_attr *d, ++ int *oa_ics, int *oa_cgr, int32_t *oa_len) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *oa_ics = !!qb_attr_code_decode(&code_fq_oa_ics, p); ++ *oa_cgr = !!qb_attr_code_decode(&code_fq_oa_cgr, p); ++ *oa_len = qb_attr_code_makesigned(&code_fq_oa_len, ++ qb_attr_code_decode(&code_fq_oa_len, p)); ++} ++ ++void qbman_fq_attr_get_mctl(struct qbman_attr *d, ++ int *bdi, int *ff, int *va, int *ps) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *bdi = !!qb_attr_code_decode(&code_fq_mctl_bdi, p); ++ *ff = !!qb_attr_code_decode(&code_fq_mctl_ff, p); ++ *va = !!qb_attr_code_decode(&code_fq_mctl_va, p); ++ *ps = !!qb_attr_code_decode(&code_fq_mctl_ps, p); ++} ++ ++void qbman_fq_attr_get_ctx(struct qbman_attr *d, uint32_t *hi, uint32_t *lo) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *hi = qb_attr_code_decode(&code_fq_ctx_upper32, p); ++ *lo = qb_attr_code_decode(&code_fq_ctx_lower32, p); ++} ++ ++void qbman_fq_attr_get_icid(struct qbman_attr *d, uint32_t *icid, int *pl) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *icid = qb_attr_code_decode(&code_fq_icid, p); ++ *pl = !!qb_attr_code_decode(&code_fq_pl, p); ++} ++ ++void qbman_fq_attr_get_vfqid(struct qbman_attr *d, uint32_t *vfqid) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *vfqid = qb_attr_code_decode(&code_fq_vfqid, p); ++} ++ ++void qbman_fq_attr_get_erfqid(struct qbman_attr *d, uint32_t *erfqid) ++{ ++ uint32_t *p = ATTR32(d); ++ ++ *erfqid = qb_attr_code_decode(&code_fq_erfqid, p); ++} ++ ++/* Query FQ Non-Programmalbe Fields */ ++static struct qb_attr_code code_fq_np_state = QB_CODE(0, 16, 3); ++static struct qb_attr_code code_fq_np_fe = QB_CODE(0, 19, 1); ++static struct qb_attr_code code_fq_np_x = QB_CODE(0, 20, 1); ++static struct qb_attr_code code_fq_np_r = QB_CODE(0, 21, 1); ++static struct qb_attr_code code_fq_np_oe = QB_CODE(0, 22, 1); ++static struct qb_attr_code code_fq_np_frm_cnt = QB_CODE(6, 0, 24); ++static struct qb_attr_code code_fq_np_byte_cnt = QB_CODE(7, 0, 32); ++ ++int qbman_fq_query_state(struct qbman_swp *s, uint32_t fqid, ++ struct qbman_attr *state) ++{ ++ uint32_t *p; ++ uint32_t verb, rslt; ++ uint32_t *d = ATTR32(state); ++ ++ qbman_fq_attr_clear(state); ++ ++ p = qbman_swp_mc_start(s); ++ if (!p) ++ return -EBUSY; ++ qb_attr_code_encode(&code_fq_fqid, p, fqid); ++ p = qbman_swp_mc_complete(s, p, QBMAN_FQ_QUERY_NP); ++ ++ /* Decode the outcome */ ++ verb = qb_attr_code_decode(&code_generic_verb, p); ++ rslt = qb_attr_code_decode(&code_generic_rslt, p); ++ BUG_ON(verb != QBMAN_FQ_QUERY_NP); ++ ++ /* Determine success or failure */ ++ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { ++ pr_err("Query NP fields of FQID 0x%x failed, code=0x%02x\n", ++ fqid, rslt); ++ return -EIO; ++ } ++ word_copy(&d[0], &p[0], 16); ++ return 0; ++} ++ ++uint32_t qbman_fq_state_schedstate(const struct qbman_attr *state) ++{ ++ const uint32_t *p = ATTR32(state); ++ ++ return qb_attr_code_decode(&code_fq_np_state, p); ++} ++ ++int qbman_fq_state_force_eligible(const struct qbman_attr *state) ++{ ++ const uint32_t *p = ATTR32(state); ++ ++ return !!qb_attr_code_decode(&code_fq_np_fe, p); ++} ++ ++int qbman_fq_state_xoff(const struct qbman_attr *state) ++{ ++ const uint32_t *p = ATTR32(state); ++ ++ return !!qb_attr_code_decode(&code_fq_np_x, p); ++} ++ ++int qbman_fq_state_retirement_pending(const struct qbman_attr *state) ++{ ++ const uint32_t *p = ATTR32(state); ++ ++ return !!qb_attr_code_decode(&code_fq_np_r, p); ++} ++ ++int qbman_fq_state_overflow_error(const struct qbman_attr *state) ++{ ++ const uint32_t *p = ATTR32(state); ++ ++ return !!qb_attr_code_decode(&code_fq_np_oe, p); ++} ++ ++uint32_t qbman_fq_state_frame_count(const struct qbman_attr *state) ++{ ++ const uint32_t *p = ATTR32(state); ++ ++ return qb_attr_code_decode(&code_fq_np_frm_cnt, p); ++} ++ ++uint32_t qbman_fq_state_byte_count(const struct qbman_attr *state) ++{ ++ const uint32_t *p = ATTR32(state); ++ ++ return qb_attr_code_decode(&code_fq_np_byte_cnt, p); ++} ++ ++/* Query CGR */ ++static struct qb_attr_code code_cgr_cgid = QB_CODE(0, 16, 16); ++static struct qb_attr_code code_cgr_cscn_wq_en_enter = QB_CODE(2, 0, 1); ++static struct qb_attr_code code_cgr_cscn_wq_en_exit = QB_CODE(2, 1, 1); ++static struct qb_attr_code code_cgr_cscn_wq_icd = QB_CODE(2, 2, 1); ++static struct qb_attr_code code_cgr_mode = QB_CODE(3, 16, 2); ++static struct qb_attr_code code_cgr_rej_cnt_mode = QB_CODE(3, 18, 1); ++static struct qb_attr_code code_cgr_cscn_bdi = QB_CODE(3, 19, 1); ++static struct qb_attr_code code_cgr_cscn_wr_en_enter = QB_CODE(3, 24, 1); ++static struct qb_attr_code code_cgr_cscn_wr_en_exit = QB_CODE(3, 25, 1); ++static struct qb_attr_code code_cgr_cg_wr_ae = QB_CODE(3, 26, 1); ++static struct qb_attr_code code_cgr_cscn_dcp_en = QB_CODE(3, 27, 1); ++static struct qb_attr_code code_cgr_cg_wr_va = QB_CODE(3, 28, 1); ++static struct qb_attr_code code_cgr_i_cnt_wr_en = QB_CODE(4, 0, 1); ++static struct qb_attr_code code_cgr_i_cnt_wr_bnd = QB_CODE(4, 1, 5); ++static struct qb_attr_code code_cgr_td_en = QB_CODE(4, 8, 1); ++static struct qb_attr_code code_cgr_cs_thres = QB_CODE(4, 16, 13); ++static struct qb_attr_code code_cgr_cs_thres_x = QB_CODE(5, 0, 13); ++static struct qb_attr_code code_cgr_td_thres = QB_CODE(5, 16, 13); ++static struct qb_attr_code code_cgr_cscn_tdcp = QB_CODE(6, 0, 16); ++static struct qb_attr_code code_cgr_cscn_wqid = QB_CODE(6, 16, 16); ++static struct qb_attr_code code_cgr_cscn_vcgid = QB_CODE(7, 0, 16); ++static struct qb_attr_code code_cgr_cg_icid = QB_CODE(7, 16, 15); ++static struct qb_attr_code code_cgr_cg_pl = QB_CODE(7, 31, 1); ++static struct qb_attr_code code_cgr_cg_wr_addr_lo = QB_CODE(8, 0, 32); ++static struct qb_attr_code code_cgr_cg_wr_addr_hi = QB_CODE(9, 0, 32); ++static struct qb_attr_code code_cgr_cscn_ctx_lo = QB_CODE(10, 0, 32); ++static struct qb_attr_code code_cgr_cscn_ctx_hi = QB_CODE(11, 0, 32); ++ ++void qbman_cgr_attr_clear(struct qbman_attr *a) ++{ ++ memset(a, 0, sizeof(*a)); ++ attr_type_set(a, qbman_attr_usage_cgr); ++} ++ ++int qbman_cgr_query(struct qbman_swp *s, uint32_t cgid, struct qbman_attr *attr) ++{ ++ uint32_t *p; ++ uint32_t verb, rslt; ++ uint32_t *d[2]; ++ int i; ++ uint32_t query_verb; ++ ++ d[0] = ATTR32(attr); ++ d[1] = ATTR32_1(attr); ++ ++ qbman_cgr_attr_clear(attr); ++ ++ for (i = 0; i < 2; i++) { ++ p = qbman_swp_mc_start(s); ++ if (!p) ++ return -EBUSY; ++ query_verb = i ? QBMAN_WRED_QUERY : QBMAN_CGR_QUERY; ++ ++ qb_attr_code_encode(&code_cgr_cgid, p, cgid); ++ p = qbman_swp_mc_complete(s, p, p[0] | query_verb); ++ ++ /* Decode the outcome */ ++ verb = qb_attr_code_decode(&code_generic_verb, p); ++ rslt = qb_attr_code_decode(&code_generic_rslt, p); ++ BUG_ON(verb != query_verb); ++ ++ /* Determine success or failure */ ++ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { ++ pr_err("Query CGID 0x%x failed,", cgid); ++ pr_err(" verb=0x%02x, code=0x%02x\n", verb, rslt); ++ return -EIO; ++ } ++ /* For the configure, word[0] of the command contains only the ++ * verb/cgid. For the query, word[0] of the result contains ++ * only the verb/rslt fields. Skip word[0] in the latter case. ++ */ ++ word_copy(&d[i][1], &p[1], 15); ++ } ++ return 0; ++} ++ ++void qbman_cgr_attr_get_ctl1(struct qbman_attr *d, int *cscn_wq_en_enter, ++ int *cscn_wq_en_exit, int *cscn_wq_icd) ++ { ++ uint32_t *p = ATTR32(d); ++ *cscn_wq_en_enter = !!qb_attr_code_decode(&code_cgr_cscn_wq_en_enter, ++ p); ++ *cscn_wq_en_exit = !!qb_attr_code_decode(&code_cgr_cscn_wq_en_exit, p); ++ *cscn_wq_icd = !!qb_attr_code_decode(&code_cgr_cscn_wq_icd, p); ++} ++ ++void qbman_cgr_attr_get_mode(struct qbman_attr *d, uint32_t *mode, ++ int *rej_cnt_mode, int *cscn_bdi) ++{ ++ uint32_t *p = ATTR32(d); ++ *mode = qb_attr_code_decode(&code_cgr_mode, p); ++ *rej_cnt_mode = !!qb_attr_code_decode(&code_cgr_rej_cnt_mode, p); ++ *cscn_bdi = !!qb_attr_code_decode(&code_cgr_cscn_bdi, p); ++} ++ ++void qbman_cgr_attr_get_ctl2(struct qbman_attr *d, int *cscn_wr_en_enter, ++ int *cscn_wr_en_exit, int *cg_wr_ae, ++ int *cscn_dcp_en, int *cg_wr_va) ++{ ++ uint32_t *p = ATTR32(d); ++ *cscn_wr_en_enter = !!qb_attr_code_decode(&code_cgr_cscn_wr_en_enter, ++ p); ++ *cscn_wr_en_exit = !!qb_attr_code_decode(&code_cgr_cscn_wr_en_exit, p); ++ *cg_wr_ae = !!qb_attr_code_decode(&code_cgr_cg_wr_ae, p); ++ *cscn_dcp_en = !!qb_attr_code_decode(&code_cgr_cscn_dcp_en, p); ++ *cg_wr_va = !!qb_attr_code_decode(&code_cgr_cg_wr_va, p); ++} ++ ++void qbman_cgr_attr_get_iwc(struct qbman_attr *d, int *i_cnt_wr_en, ++ uint32_t *i_cnt_wr_bnd) ++{ ++ uint32_t *p = ATTR32(d); ++ *i_cnt_wr_en = !!qb_attr_code_decode(&code_cgr_i_cnt_wr_en, p); ++ *i_cnt_wr_bnd = qb_attr_code_decode(&code_cgr_i_cnt_wr_bnd, p); ++} ++ ++void qbman_cgr_attr_get_tdc(struct qbman_attr *d, int *td_en) ++{ ++ uint32_t *p = ATTR32(d); ++ *td_en = !!qb_attr_code_decode(&code_cgr_td_en, p); ++} ++ ++void qbman_cgr_attr_get_cs_thres(struct qbman_attr *d, uint32_t *cs_thres) ++{ ++ uint32_t *p = ATTR32(d); ++ *cs_thres = qbman_thresh_to_value(qb_attr_code_decode( ++ &code_cgr_cs_thres, p)); ++} ++ ++void qbman_cgr_attr_get_cs_thres_x(struct qbman_attr *d, ++ uint32_t *cs_thres_x) ++{ ++ uint32_t *p = ATTR32(d); ++ *cs_thres_x = qbman_thresh_to_value(qb_attr_code_decode( ++ &code_cgr_cs_thres_x, p)); ++} ++ ++void qbman_cgr_attr_get_td_thres(struct qbman_attr *d, uint32_t *td_thres) ++{ ++ uint32_t *p = ATTR32(d); ++ *td_thres = qbman_thresh_to_value(qb_attr_code_decode( ++ &code_cgr_td_thres, p)); ++} ++ ++void qbman_cgr_attr_get_cscn_tdcp(struct qbman_attr *d, uint32_t *cscn_tdcp) ++{ ++ uint32_t *p = ATTR32(d); ++ *cscn_tdcp = qb_attr_code_decode(&code_cgr_cscn_tdcp, p); ++} ++ ++void qbman_cgr_attr_get_cscn_wqid(struct qbman_attr *d, uint32_t *cscn_wqid) ++{ ++ uint32_t *p = ATTR32(d); ++ *cscn_wqid = qb_attr_code_decode(&code_cgr_cscn_wqid, p); ++} ++ ++void qbman_cgr_attr_get_cscn_vcgid(struct qbman_attr *d, ++ uint32_t *cscn_vcgid) ++{ ++ uint32_t *p = ATTR32(d); ++ *cscn_vcgid = qb_attr_code_decode(&code_cgr_cscn_vcgid, p); ++} ++ ++void qbman_cgr_attr_get_cg_icid(struct qbman_attr *d, uint32_t *icid, ++ int *pl) ++{ ++ uint32_t *p = ATTR32(d); ++ *icid = qb_attr_code_decode(&code_cgr_cg_icid, p); ++ *pl = !!qb_attr_code_decode(&code_cgr_cg_pl, p); ++} ++ ++void qbman_cgr_attr_get_cg_wr_addr(struct qbman_attr *d, ++ uint64_t *cg_wr_addr) ++{ ++ uint32_t *p = ATTR32(d); ++ *cg_wr_addr = ((uint64_t)qb_attr_code_decode(&code_cgr_cg_wr_addr_hi, ++ p) << 32) | ++ (uint64_t)qb_attr_code_decode(&code_cgr_cg_wr_addr_lo, ++ p); ++} ++ ++void qbman_cgr_attr_get_cscn_ctx(struct qbman_attr *d, uint64_t *cscn_ctx) ++{ ++ uint32_t *p = ATTR32(d); ++ *cscn_ctx = ((uint64_t)qb_attr_code_decode(&code_cgr_cscn_ctx_hi, p) ++ << 32) | ++ (uint64_t)qb_attr_code_decode(&code_cgr_cscn_ctx_lo, p); ++} ++ ++#define WRED_EDP_WORD(n) (18 + n/4) ++#define WRED_EDP_OFFSET(n) (8 * (n % 4)) ++#define WRED_PARM_DP_WORD(n) (n + 20) ++#define WRED_WE_EDP(n) (16 + n * 2) ++#define WRED_WE_PARM_DP(n) (17 + n * 2) ++void qbman_cgr_attr_wred_get_edp(struct qbman_attr *d, uint32_t idx, ++ int *edp) ++{ ++ uint32_t *p = ATTR32(d); ++ struct qb_attr_code code_wred_edp = QB_CODE(WRED_EDP_WORD(idx), ++ WRED_EDP_OFFSET(idx), 8); ++ *edp = (int)qb_attr_code_decode(&code_wred_edp, p); ++} ++ ++void qbman_cgr_attr_wred_dp_decompose(uint32_t dp, uint64_t *minth, ++ uint64_t *maxth, uint8_t *maxp) ++{ ++ uint8_t ma, mn, step_i, step_s, pn; ++ ++ ma = (uint8_t)(dp >> 24); ++ mn = (uint8_t)(dp >> 19) & 0x1f; ++ step_i = (uint8_t)(dp >> 11); ++ step_s = (uint8_t)(dp >> 6) & 0x1f; ++ pn = (uint8_t)dp & 0x3f; ++ ++ *maxp = ((pn<<2) * 100)/256; ++ ++ if (mn == 0) ++ *maxth = ma; ++ else ++ *maxth = ((ma+256) * (1<<(mn-1))); ++ ++ if (step_s == 0) ++ *minth = *maxth - step_i; ++ else ++ *minth = *maxth - (256 + step_i) * (1<<(step_s - 1)); ++} ++ ++void qbman_cgr_attr_wred_get_parm_dp(struct qbman_attr *d, uint32_t idx, ++ uint32_t *dp) ++{ ++ uint32_t *p = ATTR32(d); ++ struct qb_attr_code code_wred_parm_dp = QB_CODE(WRED_PARM_DP_WORD(idx), ++ 0, 8); ++ *dp = qb_attr_code_decode(&code_wred_parm_dp, p); ++} ++ ++/* Query CGR/CCGR/CQ statistics */ ++static struct qb_attr_code code_cgr_stat_ct = QB_CODE(4, 0, 32); ++static struct qb_attr_code code_cgr_stat_frame_cnt_lo = QB_CODE(4, 0, 32); ++static struct qb_attr_code code_cgr_stat_frame_cnt_hi = QB_CODE(5, 0, 8); ++static struct qb_attr_code code_cgr_stat_byte_cnt_lo = QB_CODE(6, 0, 32); ++static struct qb_attr_code code_cgr_stat_byte_cnt_hi = QB_CODE(7, 0, 16); ++static int qbman_cgr_statistics_query(struct qbman_swp *s, uint32_t cgid, ++ int clear, uint32_t command_type, ++ uint64_t *frame_cnt, uint64_t *byte_cnt) ++{ ++ uint32_t *p; ++ uint32_t verb, rslt; ++ uint32_t query_verb; ++ uint32_t hi, lo; ++ ++ p = qbman_swp_mc_start(s); ++ if (!p) ++ return -EBUSY; ++ ++ qb_attr_code_encode(&code_cgr_cgid, p, cgid); ++ if (command_type < 2) ++ qb_attr_code_encode(&code_cgr_stat_ct, p, command_type); ++ query_verb = clear ? ++ QBMAN_CGR_STAT_QUERY_CLR : QBMAN_CGR_STAT_QUERY; ++ p = qbman_swp_mc_complete(s, p, p[0] | query_verb); ++ ++ /* Decode the outcome */ ++ verb = qb_attr_code_decode(&code_generic_verb, p); ++ rslt = qb_attr_code_decode(&code_generic_rslt, p); ++ BUG_ON(verb != query_verb); ++ ++ /* Determine success or failure */ ++ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { ++ pr_err("Query statistics of CGID 0x%x failed,", cgid); ++ pr_err(" verb=0x%02x code=0x%02x\n", verb, rslt); ++ return -EIO; ++ } ++ ++ if (*frame_cnt) { ++ hi = qb_attr_code_decode(&code_cgr_stat_frame_cnt_hi, p); ++ lo = qb_attr_code_decode(&code_cgr_stat_frame_cnt_lo, p); ++ *frame_cnt = ((uint64_t)hi << 32) | (uint64_t)lo; ++ } ++ if (*byte_cnt) { ++ hi = qb_attr_code_decode(&code_cgr_stat_byte_cnt_hi, p); ++ lo = qb_attr_code_decode(&code_cgr_stat_byte_cnt_lo, p); ++ *byte_cnt = ((uint64_t)hi << 32) | (uint64_t)lo; ++ } ++ ++ return 0; ++} ++ ++int qbman_cgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, ++ uint64_t *frame_cnt, uint64_t *byte_cnt) ++{ ++ return qbman_cgr_statistics_query(s, cgid, clear, 0xff, ++ frame_cnt, byte_cnt); ++} ++ ++int qbman_ccgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, ++ uint64_t *frame_cnt, uint64_t *byte_cnt) ++{ ++ return qbman_cgr_statistics_query(s, cgid, clear, 1, ++ frame_cnt, byte_cnt); ++} ++ ++int qbman_cq_dequeue_statistics(struct qbman_swp *s, uint32_t cgid, int clear, ++ uint64_t *frame_cnt, uint64_t *byte_cnt) ++{ ++ return qbman_cgr_statistics_query(s, cgid, clear, 0, ++ frame_cnt, byte_cnt); ++} +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/qbman_debug.h +@@ -0,0 +1,136 @@ ++/* Copyright (C) 2015 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. ++ */ ++ ++struct qbman_attr { ++ uint32_t dont_manipulate_directly[40]; ++}; ++ ++/* Buffer pool query commands */ ++int qbman_bp_query(struct qbman_swp *s, uint32_t bpid, ++ struct qbman_attr *a); ++void qbman_bp_attr_get_bdi(struct qbman_attr *a, int *bdi, int *va, int *wae); ++void qbman_bp_attr_get_swdet(struct qbman_attr *a, uint32_t *swdet); ++void qbman_bp_attr_get_swdxt(struct qbman_attr *a, uint32_t *swdxt); ++void qbman_bp_attr_get_hwdet(struct qbman_attr *a, uint32_t *hwdet); ++void qbman_bp_attr_get_hwdxt(struct qbman_attr *a, uint32_t *hwdxt); ++void qbman_bp_attr_get_swset(struct qbman_attr *a, uint32_t *swset); ++void qbman_bp_attr_get_swsxt(struct qbman_attr *a, uint32_t *swsxt); ++void qbman_bp_attr_get_vbpid(struct qbman_attr *a, uint32_t *vbpid); ++void qbman_bp_attr_get_icid(struct qbman_attr *a, uint32_t *icid, int *pl); ++void qbman_bp_attr_get_bpscn_addr(struct qbman_attr *a, uint64_t *bpscn_addr); ++void qbman_bp_attr_get_bpscn_ctx(struct qbman_attr *a, uint64_t *bpscn_ctx); ++void qbman_bp_attr_get_hw_targ(struct qbman_attr *a, uint32_t *hw_targ); ++int qbman_bp_info_has_free_bufs(struct qbman_attr *a); ++int qbman_bp_info_is_depleted(struct qbman_attr *a); ++int qbman_bp_info_is_surplus(struct qbman_attr *a); ++uint32_t qbman_bp_info_num_free_bufs(struct qbman_attr *a); ++uint32_t qbman_bp_info_hdptr(struct qbman_attr *a); ++uint32_t qbman_bp_info_sdcnt(struct qbman_attr *a); ++uint32_t qbman_bp_info_hdcnt(struct qbman_attr *a); ++uint32_t qbman_bp_info_sscnt(struct qbman_attr *a); ++ ++/* FQ query function for programmable fields */ ++int qbman_fq_query(struct qbman_swp *s, uint32_t fqid, ++ struct qbman_attr *desc); ++void qbman_fq_attr_get_fqctrl(struct qbman_attr *d, uint32_t *fqctrl); ++void qbman_fq_attr_get_cgrid(struct qbman_attr *d, uint32_t *cgrid); ++void qbman_fq_attr_get_destwq(struct qbman_attr *d, uint32_t *destwq); ++void qbman_fq_attr_get_icscred(struct qbman_attr *d, uint32_t *icscred); ++void qbman_fq_attr_get_tdthresh(struct qbman_attr *d, uint32_t *tdthresh); ++void qbman_fq_attr_get_oa(struct qbman_attr *d, ++ int *oa_ics, int *oa_cgr, int32_t *oa_len); ++void qbman_fq_attr_get_mctl(struct qbman_attr *d, ++ int *bdi, int *ff, int *va, int *ps); ++void qbman_fq_attr_get_ctx(struct qbman_attr *d, uint32_t *hi, uint32_t *lo); ++void qbman_fq_attr_get_icid(struct qbman_attr *d, uint32_t *icid, int *pl); ++void qbman_fq_attr_get_vfqid(struct qbman_attr *d, uint32_t *vfqid); ++void qbman_fq_attr_get_erfqid(struct qbman_attr *d, uint32_t *erfqid); ++ ++/* FQ query command for non-programmable fields*/ ++enum qbman_fq_schedstate_e { ++ qbman_fq_schedstate_oos = 0, ++ qbman_fq_schedstate_retired, ++ qbman_fq_schedstate_tentatively_scheduled, ++ qbman_fq_schedstate_truly_scheduled, ++ qbman_fq_schedstate_parked, ++ qbman_fq_schedstate_held_active, ++}; ++ ++int qbman_fq_query_state(struct qbman_swp *s, uint32_t fqid, ++ struct qbman_attr *state); ++uint32_t qbman_fq_state_schedstate(const struct qbman_attr *state); ++int qbman_fq_state_force_eligible(const struct qbman_attr *state); ++int qbman_fq_state_xoff(const struct qbman_attr *state); ++int qbman_fq_state_retirement_pending(const struct qbman_attr *state); ++int qbman_fq_state_overflow_error(const struct qbman_attr *state); ++uint32_t qbman_fq_state_frame_count(const struct qbman_attr *state); ++uint32_t qbman_fq_state_byte_count(const struct qbman_attr *state); ++ ++/* CGR query */ ++int qbman_cgr_query(struct qbman_swp *s, uint32_t cgid, ++ struct qbman_attr *attr); ++void qbman_cgr_attr_get_ctl1(struct qbman_attr *d, int *cscn_wq_en_enter, ++ int *cscn_wq_en_exit, int *cscn_wq_icd); ++void qbman_cgr_attr_get_mode(struct qbman_attr *d, uint32_t *mode, ++ int *rej_cnt_mode, int *cscn_bdi); ++void qbman_cgr_attr_get_ctl2(struct qbman_attr *d, int *cscn_wr_en_enter, ++ int *cscn_wr_en_exit, int *cg_wr_ae, ++ int *cscn_dcp_en, int *cg_wr_va); ++void qbman_cgr_attr_get_iwc(struct qbman_attr *d, int *i_cnt_wr_en, ++ uint32_t *i_cnt_wr_bnd); ++void qbman_cgr_attr_get_tdc(struct qbman_attr *d, int *td_en); ++void qbman_cgr_attr_get_cs_thres(struct qbman_attr *d, uint32_t *cs_thres); ++void qbman_cgr_attr_get_cs_thres_x(struct qbman_attr *d, ++ uint32_t *cs_thres_x); ++void qbman_cgr_attr_get_td_thres(struct qbman_attr *d, uint32_t *td_thres); ++void qbman_cgr_attr_get_cscn_tdcp(struct qbman_attr *d, uint32_t *cscn_tdcp); ++void qbman_cgr_attr_get_cscn_wqid(struct qbman_attr *d, uint32_t *cscn_wqid); ++void qbman_cgr_attr_get_cscn_vcgid(struct qbman_attr *d, ++ uint32_t *cscn_vcgid); ++void qbman_cgr_attr_get_cg_icid(struct qbman_attr *d, uint32_t *icid, ++ int *pl); ++void qbman_cgr_attr_get_cg_wr_addr(struct qbman_attr *d, ++ uint64_t *cg_wr_addr); ++void qbman_cgr_attr_get_cscn_ctx(struct qbman_attr *d, uint64_t *cscn_ctx); ++void qbman_cgr_attr_wred_get_edp(struct qbman_attr *d, uint32_t idx, ++ int *edp); ++void qbman_cgr_attr_wred_dp_decompose(uint32_t dp, uint64_t *minth, ++ uint64_t *maxth, uint8_t *maxp); ++void qbman_cgr_attr_wred_get_parm_dp(struct qbman_attr *d, uint32_t idx, ++ uint32_t *dp); ++ ++/* CGR/CCGR/CQ statistics query */ ++int qbman_cgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, ++ uint64_t *frame_cnt, uint64_t *byte_cnt); ++int qbman_ccgr_reject_statistics(struct qbman_swp *s, uint32_t cgid, int clear, ++ uint64_t *frame_cnt, uint64_t *byte_cnt); ++int qbman_cq_dequeue_statistics(struct qbman_swp *s, uint32_t cgid, int clear, ++ uint64_t *frame_cnt, uint64_t *byte_cnt); +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.c +@@ -0,0 +1,1212 @@ ++/* Copyright (C) 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. ++ */ ++ ++#include "qbman_portal.h" ++ ++/* QBMan portal management command codes */ ++#define QBMAN_MC_ACQUIRE 0x30 ++#define QBMAN_WQCHAN_CONFIGURE 0x46 ++ ++/* CINH register offsets */ ++#define QBMAN_CINH_SWP_EQAR 0x8c0 ++#define QBMAN_CINH_SWP_DQPI 0xa00 ++#define QBMAN_CINH_SWP_DCAP 0xac0 ++#define QBMAN_CINH_SWP_SDQCR 0xb00 ++#define QBMAN_CINH_SWP_RAR 0xcc0 ++#define QBMAN_CINH_SWP_ISR 0xe00 ++#define QBMAN_CINH_SWP_IER 0xe40 ++#define QBMAN_CINH_SWP_ISDR 0xe80 ++#define QBMAN_CINH_SWP_IIR 0xec0 ++ ++/* CENA register offsets */ ++#define QBMAN_CENA_SWP_EQCR(n) (0x000 + ((uint32_t)(n) << 6)) ++#define QBMAN_CENA_SWP_DQRR(n) (0x200 + ((uint32_t)(n) << 6)) ++#define QBMAN_CENA_SWP_RCR(n) (0x400 + ((uint32_t)(n) << 6)) ++#define QBMAN_CENA_SWP_CR 0x600 ++#define QBMAN_CENA_SWP_RR(vb) (0x700 + ((uint32_t)(vb) >> 1)) ++#define QBMAN_CENA_SWP_VDQCR 0x780 ++ ++/* Reverse mapping of QBMAN_CENA_SWP_DQRR() */ ++#define QBMAN_IDX_FROM_DQRR(p) (((unsigned long)p & 0x1ff) >> 6) ++ ++/* QBMan FQ management command codes */ ++#define QBMAN_FQ_SCHEDULE 0x48 ++#define QBMAN_FQ_FORCE 0x49 ++#define QBMAN_FQ_XON 0x4d ++#define QBMAN_FQ_XOFF 0x4e ++ ++/*******************************/ ++/* Pre-defined attribute codes */ ++/*******************************/ ++ ++struct qb_attr_code code_generic_verb = QB_CODE(0, 0, 7); ++struct qb_attr_code code_generic_rslt = QB_CODE(0, 8, 8); ++ ++/*************************/ ++/* SDQCR attribute codes */ ++/*************************/ ++ ++/* we put these here because at least some of them are required by ++ * qbman_swp_init() */ ++struct qb_attr_code code_sdqcr_dct = QB_CODE(0, 24, 2); ++struct qb_attr_code code_sdqcr_fc = QB_CODE(0, 29, 1); ++struct qb_attr_code code_sdqcr_tok = QB_CODE(0, 16, 8); ++#define CODE_SDQCR_DQSRC(n) QB_CODE(0, n, 1) ++enum qbman_sdqcr_dct { ++ qbman_sdqcr_dct_null = 0, ++ qbman_sdqcr_dct_prio_ics, ++ qbman_sdqcr_dct_active_ics, ++ qbman_sdqcr_dct_active ++}; ++enum qbman_sdqcr_fc { ++ qbman_sdqcr_fc_one = 0, ++ qbman_sdqcr_fc_up_to_3 = 1 ++}; ++struct qb_attr_code code_sdqcr_dqsrc = QB_CODE(0, 0, 16); ++ ++/*********************************/ ++/* Portal constructor/destructor */ ++/*********************************/ ++ ++/* Software portals should always be in the power-on state when we initialise, ++ * due to the CCSR-based portal reset functionality that MC has. ++ * ++ * Erk! Turns out that QMan versions prior to 4.1 do not correctly reset DQRR ++ * valid-bits, so we need to support a workaround where we don't trust ++ * valid-bits when detecting new entries until any stale ring entries have been ++ * overwritten at least once. The idea is that we read PI for the first few ++ * entries, then switch to valid-bit after that. The trick is to clear the ++ * bug-work-around boolean once the PI wraps around the ring for the first time. ++ * ++ * Note: this still carries a slight additional cost once the decrementer hits ++ * zero, so ideally the workaround should only be compiled in if the compiled ++ * image needs to support affected chips. We use WORKAROUND_DQRR_RESET_BUG for ++ * this. ++ */ ++struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) ++{ ++ int ret; ++ struct qbman_swp *p = kmalloc(sizeof(*p), GFP_KERNEL); ++ ++ if (!p) ++ return NULL; ++ p->desc = d; ++#ifdef QBMAN_CHECKING ++ p->mc.check = swp_mc_can_start; ++#endif ++ p->mc.valid_bit = QB_VALID_BIT; ++ p->sdq = 0; ++ qb_attr_code_encode(&code_sdqcr_dct, &p->sdq, qbman_sdqcr_dct_prio_ics); ++ qb_attr_code_encode(&code_sdqcr_fc, &p->sdq, qbman_sdqcr_fc_up_to_3); ++ qb_attr_code_encode(&code_sdqcr_tok, &p->sdq, 0xbb); ++ atomic_set(&p->vdq.busy, 1); ++ p->vdq.valid_bit = QB_VALID_BIT; ++ p->dqrr.next_idx = 0; ++ p->dqrr.valid_bit = QB_VALID_BIT; ++ /* TODO: should also read PI/CI type registers and check that they're on ++ * PoR values. If we're asked to initialise portals that aren't in reset ++ * state, bad things will follow. */ ++#ifdef WORKAROUND_DQRR_RESET_BUG ++ p->dqrr.reset_bug = 1; ++#endif ++ if ((p->desc->qman_version & 0xFFFF0000) < QMAN_REV_4100) ++ p->dqrr.dqrr_size = 4; ++ else ++ p->dqrr.dqrr_size = 8; ++ ret = qbman_swp_sys_init(&p->sys, d, p->dqrr.dqrr_size); ++ if (ret) { ++ kfree(p); ++ pr_err("qbman_swp_sys_init() failed %d\n", ret); ++ return NULL; ++ } ++ /* SDQCR needs to be initialized to 0 when no channels are ++ being dequeued from or else the QMan HW will indicate an ++ error. The values that were calculated above will be ++ applied when dequeues from a specific channel are enabled */ ++ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_SDQCR, 0); ++ return p; ++} ++ ++void qbman_swp_finish(struct qbman_swp *p) ++{ ++#ifdef QBMAN_CHECKING ++ BUG_ON(p->mc.check != swp_mc_can_start); ++#endif ++ qbman_swp_sys_finish(&p->sys); ++ kfree(p); ++} ++ ++const struct qbman_swp_desc *qbman_swp_get_desc(struct qbman_swp *p) ++{ ++ return p->desc; ++} ++ ++/**************/ ++/* Interrupts */ ++/**************/ ++ ++uint32_t qbman_swp_interrupt_get_vanish(struct qbman_swp *p) ++{ ++ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ISDR); ++} ++ ++void qbman_swp_interrupt_set_vanish(struct qbman_swp *p, uint32_t mask) ++{ ++ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ISDR, mask); ++} ++ ++uint32_t qbman_swp_interrupt_read_status(struct qbman_swp *p) ++{ ++ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ISR); ++} ++ ++void qbman_swp_interrupt_clear_status(struct qbman_swp *p, uint32_t mask) ++{ ++ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ISR, mask); ++} ++ ++uint32_t qbman_swp_interrupt_get_trigger(struct qbman_swp *p) ++{ ++ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_IER); ++} ++ ++void qbman_swp_interrupt_set_trigger(struct qbman_swp *p, uint32_t mask) ++{ ++ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_IER, mask); ++} ++ ++int qbman_swp_interrupt_get_inhibit(struct qbman_swp *p) ++{ ++ return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_IIR); ++} ++ ++void qbman_swp_interrupt_set_inhibit(struct qbman_swp *p, int inhibit) ++{ ++ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_IIR, inhibit ? 0xffffffff : 0); ++} ++ ++/***********************/ ++/* Management commands */ ++/***********************/ ++ ++/* ++ * Internal code common to all types of management commands. ++ */ ++ ++void *qbman_swp_mc_start(struct qbman_swp *p) ++{ ++ void *ret; ++#ifdef QBMAN_CHECKING ++ BUG_ON(p->mc.check != swp_mc_can_start); ++#endif ++ ret = qbman_cena_write_start(&p->sys, QBMAN_CENA_SWP_CR); ++#ifdef QBMAN_CHECKING ++ if (!ret) ++ p->mc.check = swp_mc_can_submit; ++#endif ++ return ret; ++} ++ ++void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint32_t cmd_verb) ++{ ++ uint32_t *v = cmd; ++#ifdef QBMAN_CHECKING ++ BUG_ON(!p->mc.check != swp_mc_can_submit); ++#endif ++ /* TBD: "|=" is going to hurt performance. Need to move as many fields ++ * out of word zero, and for those that remain, the "OR" needs to occur ++ * at the caller side. This debug check helps to catch cases where the ++ * caller wants to OR but has forgotten to do so. */ ++ BUG_ON((*v & cmd_verb) != *v); ++ *v = cmd_verb | p->mc.valid_bit; ++ qbman_cena_write_complete(&p->sys, QBMAN_CENA_SWP_CR, cmd); ++#ifdef QBMAN_CHECKING ++ p->mc.check = swp_mc_can_poll; ++#endif ++} ++ ++void *qbman_swp_mc_result(struct qbman_swp *p) ++{ ++ uint32_t *ret, verb; ++#ifdef QBMAN_CHECKING ++ BUG_ON(p->mc.check != swp_mc_can_poll); ++#endif ++ qbman_cena_invalidate_prefetch(&p->sys, ++ QBMAN_CENA_SWP_RR(p->mc.valid_bit)); ++ ret = qbman_cena_read(&p->sys, QBMAN_CENA_SWP_RR(p->mc.valid_bit)); ++ /* Remove the valid-bit - command completed iff the rest is non-zero */ ++ verb = ret[0] & ~QB_VALID_BIT; ++ if (!verb) ++ return NULL; ++#ifdef QBMAN_CHECKING ++ p->mc.check = swp_mc_can_start; ++#endif ++ p->mc.valid_bit ^= QB_VALID_BIT; ++ return ret; ++} ++ ++/***********/ ++/* Enqueue */ ++/***********/ ++ ++/* These should be const, eventually */ ++static struct qb_attr_code code_eq_cmd = QB_CODE(0, 0, 2); ++static struct qb_attr_code code_eq_eqdi = QB_CODE(0, 3, 1); ++static struct qb_attr_code code_eq_dca_en = QB_CODE(0, 15, 1); ++static struct qb_attr_code code_eq_dca_pk = QB_CODE(0, 14, 1); ++static struct qb_attr_code code_eq_dca_idx = QB_CODE(0, 8, 2); ++static struct qb_attr_code code_eq_orp_en = QB_CODE(0, 2, 1); ++static struct qb_attr_code code_eq_orp_is_nesn = QB_CODE(0, 31, 1); ++static struct qb_attr_code code_eq_orp_nlis = QB_CODE(0, 30, 1); ++static struct qb_attr_code code_eq_orp_seqnum = QB_CODE(0, 16, 14); ++static struct qb_attr_code code_eq_opr_id = QB_CODE(1, 0, 16); ++static struct qb_attr_code code_eq_tgt_id = QB_CODE(2, 0, 24); ++/* static struct qb_attr_code code_eq_tag = QB_CODE(3, 0, 32); */ ++static struct qb_attr_code code_eq_qd_en = QB_CODE(0, 4, 1); ++static struct qb_attr_code code_eq_qd_bin = QB_CODE(4, 0, 16); ++static struct qb_attr_code code_eq_qd_pri = QB_CODE(4, 16, 4); ++static struct qb_attr_code code_eq_rsp_stash = QB_CODE(5, 16, 1); ++static struct qb_attr_code code_eq_rsp_id = QB_CODE(5, 24, 8); ++static struct qb_attr_code code_eq_rsp_lo = QB_CODE(6, 0, 32); ++ ++enum qbman_eq_cmd_e { ++ /* No enqueue, primarily for plugging ORP gaps for dropped frames */ ++ qbman_eq_cmd_empty, ++ /* DMA an enqueue response once complete */ ++ qbman_eq_cmd_respond, ++ /* DMA an enqueue response only if the enqueue fails */ ++ qbman_eq_cmd_respond_reject ++}; ++ ++void qbman_eq_desc_clear(struct qbman_eq_desc *d) ++{ ++ memset(d, 0, sizeof(*d)); ++} ++ ++void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_orp_en, cl, 0); ++ qb_attr_code_encode(&code_eq_cmd, cl, ++ respond_success ? qbman_eq_cmd_respond : ++ qbman_eq_cmd_respond_reject); ++} ++ ++void qbman_eq_desc_set_orp(struct qbman_eq_desc *d, int respond_success, ++ uint32_t opr_id, uint32_t seqnum, int incomplete) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_orp_en, cl, 1); ++ qb_attr_code_encode(&code_eq_cmd, cl, ++ respond_success ? qbman_eq_cmd_respond : ++ qbman_eq_cmd_respond_reject); ++ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); ++ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); ++ qb_attr_code_encode(&code_eq_orp_nlis, cl, !!incomplete); ++} ++ ++void qbman_eq_desc_set_orp_hole(struct qbman_eq_desc *d, uint32_t opr_id, ++ uint32_t seqnum) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_orp_en, cl, 1); ++ qb_attr_code_encode(&code_eq_cmd, cl, qbman_eq_cmd_empty); ++ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); ++ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); ++ qb_attr_code_encode(&code_eq_orp_nlis, cl, 0); ++ qb_attr_code_encode(&code_eq_orp_is_nesn, cl, 0); ++} ++ ++void qbman_eq_desc_set_orp_nesn(struct qbman_eq_desc *d, uint32_t opr_id, ++ uint32_t seqnum) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_orp_en, cl, 1); ++ qb_attr_code_encode(&code_eq_cmd, cl, qbman_eq_cmd_empty); ++ qb_attr_code_encode(&code_eq_opr_id, cl, opr_id); ++ qb_attr_code_encode(&code_eq_orp_seqnum, cl, seqnum); ++ qb_attr_code_encode(&code_eq_orp_nlis, cl, 0); ++ qb_attr_code_encode(&code_eq_orp_is_nesn, cl, 1); ++} ++ ++void qbman_eq_desc_set_response(struct qbman_eq_desc *d, ++ dma_addr_t storage_phys, ++ int stash) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode_64(&code_eq_rsp_lo, (uint64_t *)cl, storage_phys); ++ qb_attr_code_encode(&code_eq_rsp_stash, cl, !!stash); ++} ++ ++void qbman_eq_desc_set_token(struct qbman_eq_desc *d, uint8_t token) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_rsp_id, cl, (uint32_t)token); ++} ++ ++void qbman_eq_desc_set_fq(struct qbman_eq_desc *d, uint32_t fqid) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_qd_en, cl, 0); ++ qb_attr_code_encode(&code_eq_tgt_id, cl, fqid); ++} ++ ++void qbman_eq_desc_set_qd(struct qbman_eq_desc *d, uint32_t qdid, ++ uint32_t qd_bin, uint32_t qd_prio) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_qd_en, cl, 1); ++ qb_attr_code_encode(&code_eq_tgt_id, cl, qdid); ++ qb_attr_code_encode(&code_eq_qd_bin, cl, qd_bin); ++ qb_attr_code_encode(&code_eq_qd_pri, cl, qd_prio); ++} ++ ++void qbman_eq_desc_set_eqdi(struct qbman_eq_desc *d, int enable) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_eqdi, cl, !!enable); ++} ++ ++void qbman_eq_desc_set_dca(struct qbman_eq_desc *d, int enable, ++ uint32_t dqrr_idx, int park) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_eq_dca_en, cl, !!enable); ++ if (enable) { ++ qb_attr_code_encode(&code_eq_dca_pk, cl, !!park); ++ qb_attr_code_encode(&code_eq_dca_idx, cl, dqrr_idx); ++ } ++} ++ ++#define EQAR_IDX(eqar) ((eqar) & 0x7) ++#define EQAR_VB(eqar) ((eqar) & 0x80) ++#define EQAR_SUCCESS(eqar) ((eqar) & 0x100) ++ ++int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d, ++ const struct qbman_fd *fd) ++{ ++ uint32_t *p; ++ const uint32_t *cl = qb_cl(d); ++ uint32_t eqar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_EQAR); ++ ++ pr_debug("EQAR=%08x\n", eqar); ++ if (!EQAR_SUCCESS(eqar)) ++ return -EBUSY; ++ p = qbman_cena_write_start(&s->sys, ++ QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar))); ++ word_copy(&p[1], &cl[1], 7); ++ word_copy(&p[8], fd, sizeof(*fd) >> 2); ++ /* Set the verb byte, have to substitute in the valid-bit */ ++ p[0] = cl[0] | EQAR_VB(eqar); ++ qbman_cena_write_complete(&s->sys, ++ QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar)), ++ p); ++ return 0; ++} ++ ++/*************************/ ++/* Static (push) dequeue */ ++/*************************/ ++ ++void qbman_swp_push_get(struct qbman_swp *s, uint8_t channel_idx, int *enabled) ++{ ++ struct qb_attr_code code = CODE_SDQCR_DQSRC(channel_idx); ++ ++ BUG_ON(channel_idx > 15); ++ *enabled = (int)qb_attr_code_decode(&code, &s->sdq); ++} ++ ++void qbman_swp_push_set(struct qbman_swp *s, uint8_t channel_idx, int enable) ++{ ++ uint16_t dqsrc; ++ struct qb_attr_code code = CODE_SDQCR_DQSRC(channel_idx); ++ ++ BUG_ON(channel_idx > 15); ++ qb_attr_code_encode(&code, &s->sdq, !!enable); ++ /* Read make the complete src map. If no channels are enabled ++ the SDQCR must be 0 or else QMan will assert errors */ ++ dqsrc = (uint16_t)qb_attr_code_decode(&code_sdqcr_dqsrc, &s->sdq); ++ if (dqsrc != 0) ++ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_SDQCR, s->sdq); ++ else ++ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_SDQCR, 0); ++} ++ ++/***************************/ ++/* Volatile (pull) dequeue */ ++/***************************/ ++ ++/* These should be const, eventually */ ++static struct qb_attr_code code_pull_dct = QB_CODE(0, 0, 2); ++static struct qb_attr_code code_pull_dt = QB_CODE(0, 2, 2); ++static struct qb_attr_code code_pull_rls = QB_CODE(0, 4, 1); ++static struct qb_attr_code code_pull_stash = QB_CODE(0, 5, 1); ++static struct qb_attr_code code_pull_numframes = QB_CODE(0, 8, 4); ++static struct qb_attr_code code_pull_token = QB_CODE(0, 16, 8); ++static struct qb_attr_code code_pull_dqsource = QB_CODE(1, 0, 24); ++static struct qb_attr_code code_pull_rsp_lo = QB_CODE(2, 0, 32); ++ ++enum qb_pull_dt_e { ++ qb_pull_dt_channel, ++ qb_pull_dt_workqueue, ++ qb_pull_dt_framequeue ++}; ++ ++void qbman_pull_desc_clear(struct qbman_pull_desc *d) ++{ ++ memset(d, 0, sizeof(*d)); ++} ++ ++void qbman_pull_desc_set_storage(struct qbman_pull_desc *d, ++ struct dpaa2_dq *storage, ++ dma_addr_t storage_phys, ++ int stash) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ /* Squiggle the pointer 'storage' into the extra 2 words of the ++ * descriptor (which aren't copied to the hw command) */ ++ *(void **)&cl[4] = storage; ++ if (!storage) { ++ qb_attr_code_encode(&code_pull_rls, cl, 0); ++ return; ++ } ++ qb_attr_code_encode(&code_pull_rls, cl, 1); ++ qb_attr_code_encode(&code_pull_stash, cl, !!stash); ++ qb_attr_code_encode_64(&code_pull_rsp_lo, (uint64_t *)cl, storage_phys); ++} ++ ++void qbman_pull_desc_set_numframes(struct qbman_pull_desc *d, uint8_t numframes) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ BUG_ON(!numframes || (numframes > 16)); ++ qb_attr_code_encode(&code_pull_numframes, cl, ++ (uint32_t)(numframes - 1)); ++} ++ ++void qbman_pull_desc_set_token(struct qbman_pull_desc *d, uint8_t token) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_pull_token, cl, token); ++} ++ ++void qbman_pull_desc_set_fq(struct qbman_pull_desc *d, uint32_t fqid) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_pull_dct, cl, 1); ++ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_framequeue); ++ qb_attr_code_encode(&code_pull_dqsource, cl, fqid); ++} ++ ++void qbman_pull_desc_set_wq(struct qbman_pull_desc *d, uint32_t wqid, ++ enum qbman_pull_type_e dct) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_pull_dct, cl, dct); ++ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_workqueue); ++ qb_attr_code_encode(&code_pull_dqsource, cl, wqid); ++} ++ ++void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, uint32_t chid, ++ enum qbman_pull_type_e dct) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_pull_dct, cl, dct); ++ qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_channel); ++ qb_attr_code_encode(&code_pull_dqsource, cl, chid); ++} ++ ++int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d) ++{ ++ uint32_t *p; ++ uint32_t *cl = qb_cl(d); ++ ++ if (!atomic_dec_and_test(&s->vdq.busy)) { ++ atomic_inc(&s->vdq.busy); ++ return -EBUSY; ++ } ++ s->vdq.storage = *(void **)&cl[4]; ++ qb_attr_code_encode(&code_pull_token, cl, 1); ++ p = qbman_cena_write_start(&s->sys, QBMAN_CENA_SWP_VDQCR); ++ word_copy(&p[1], &cl[1], 3); ++ /* Set the verb byte, have to substitute in the valid-bit */ ++ p[0] = cl[0] | s->vdq.valid_bit; ++ s->vdq.valid_bit ^= QB_VALID_BIT; ++ qbman_cena_write_complete(&s->sys, QBMAN_CENA_SWP_VDQCR, p); ++ return 0; ++} ++ ++/****************/ ++/* Polling DQRR */ ++/****************/ ++ ++static struct qb_attr_code code_dqrr_verb = QB_CODE(0, 0, 8); ++static struct qb_attr_code code_dqrr_response = QB_CODE(0, 0, 7); ++static struct qb_attr_code code_dqrr_stat = QB_CODE(0, 8, 8); ++static struct qb_attr_code code_dqrr_seqnum = QB_CODE(0, 16, 14); ++static struct qb_attr_code code_dqrr_odpid = QB_CODE(1, 0, 16); ++/* static struct qb_attr_code code_dqrr_tok = QB_CODE(1, 24, 8); */ ++static struct qb_attr_code code_dqrr_fqid = QB_CODE(2, 0, 24); ++static struct qb_attr_code code_dqrr_byte_count = QB_CODE(4, 0, 32); ++static struct qb_attr_code code_dqrr_frame_count = QB_CODE(5, 0, 24); ++static struct qb_attr_code code_dqrr_ctx_lo = QB_CODE(6, 0, 32); ++ ++#define QBMAN_RESULT_DQ 0x60 ++#define QBMAN_RESULT_FQRN 0x21 ++#define QBMAN_RESULT_FQRNI 0x22 ++#define QBMAN_RESULT_FQPN 0x24 ++#define QBMAN_RESULT_FQDAN 0x25 ++#define QBMAN_RESULT_CDAN 0x26 ++#define QBMAN_RESULT_CSCN_MEM 0x27 ++#define QBMAN_RESULT_CGCU 0x28 ++#define QBMAN_RESULT_BPSCN 0x29 ++#define QBMAN_RESULT_CSCN_WQ 0x2a ++ ++static struct qb_attr_code code_dqpi_pi = QB_CODE(0, 0, 4); ++ ++/* NULL return if there are no unconsumed DQRR entries. Returns a DQRR entry ++ * only once, so repeated calls can return a sequence of DQRR entries, without ++ * requiring they be consumed immediately or in any particular order. */ ++const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s) ++{ ++ uint32_t verb; ++ uint32_t response_verb; ++ uint32_t flags; ++ const struct dpaa2_dq *dq; ++ const uint32_t *p; ++ ++ /* Before using valid-bit to detect if something is there, we have to ++ * handle the case of the DQRR reset bug... */ ++#ifdef WORKAROUND_DQRR_RESET_BUG ++ if (unlikely(s->dqrr.reset_bug)) { ++ /* We pick up new entries by cache-inhibited producer index, ++ * which means that a non-coherent mapping would require us to ++ * invalidate and read *only* once that PI has indicated that ++ * there's an entry here. The first trip around the DQRR ring ++ * will be much less efficient than all subsequent trips around ++ * it... ++ */ ++ uint32_t dqpi = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_DQPI); ++ uint32_t pi = qb_attr_code_decode(&code_dqpi_pi, &dqpi); ++ /* there are new entries iff pi != next_idx */ ++ if (pi == s->dqrr.next_idx) ++ return NULL; ++ /* if next_idx is/was the last ring index, and 'pi' is ++ * different, we can disable the workaround as all the ring ++ * entries have now been DMA'd to so valid-bit checking is ++ * repaired. Note: this logic needs to be based on next_idx ++ * (which increments one at a time), rather than on pi (which ++ * can burst and wrap-around between our snapshots of it). ++ */ ++ if (s->dqrr.next_idx == (s->dqrr.dqrr_size - 1)) { ++ pr_debug("DEBUG: next_idx=%d, pi=%d, clear reset bug\n", ++ s->dqrr.next_idx, pi); ++ s->dqrr.reset_bug = 0; ++ } ++ qbman_cena_invalidate_prefetch(&s->sys, ++ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); ++ } ++#endif ++ ++ dq = qbman_cena_read(&s->sys, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); ++ p = qb_cl(dq); ++ verb = qb_attr_code_decode(&code_dqrr_verb, p); ++ ++ /* If the valid-bit isn't of the expected polarity, nothing there. Note, ++ * in the DQRR reset bug workaround, we shouldn't need to skip these ++ * check, because we've already determined that a new entry is available ++ * and we've invalidated the cacheline before reading it, so the ++ * valid-bit behaviour is repaired and should tell us what we already ++ * knew from reading PI. ++ */ ++ if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) { ++ qbman_cena_invalidate_prefetch(&s->sys, ++ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); ++ return NULL; ++ } ++ /* There's something there. Move "next_idx" attention to the next ring ++ * entry (and prefetch it) before returning what we found. */ ++ s->dqrr.next_idx++; ++ s->dqrr.next_idx &= s->dqrr.dqrr_size - 1; /* Wrap around */ ++ /* TODO: it's possible to do all this without conditionals, optimise it ++ * later. */ ++ if (!s->dqrr.next_idx) ++ s->dqrr.valid_bit ^= QB_VALID_BIT; ++ ++ /* If this is the final response to a volatile dequeue command ++ indicate that the vdq is no longer busy */ ++ flags = dpaa2_dq_flags(dq); ++ response_verb = qb_attr_code_decode(&code_dqrr_response, &verb); ++ if ((response_verb == QBMAN_RESULT_DQ) && ++ (flags & DPAA2_DQ_STAT_VOLATILE) && ++ (flags & DPAA2_DQ_STAT_EXPIRED)) ++ atomic_inc(&s->vdq.busy); ++ ++ qbman_cena_invalidate_prefetch(&s->sys, ++ QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); ++ return dq; ++} ++ ++/* Consume DQRR entries previously returned from qbman_swp_dqrr_next(). */ ++void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct dpaa2_dq *dq) ++{ ++ qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_DCAP, QBMAN_IDX_FROM_DQRR(dq)); ++} ++ ++/*********************************/ ++/* Polling user-provided storage */ ++/*********************************/ ++ ++int qbman_result_has_new_result(struct qbman_swp *s, ++ const struct dpaa2_dq *dq) ++{ ++ /* To avoid converting the little-endian DQ entry to host-endian prior ++ * to us knowing whether there is a valid entry or not (and run the ++ * risk of corrupting the incoming hardware LE write), we detect in ++ * hardware endianness rather than host. This means we need a different ++ * "code" depending on whether we are BE or LE in software, which is ++ * where DQRR_TOK_OFFSET comes in... */ ++ static struct qb_attr_code code_dqrr_tok_detect = ++ QB_CODE(0, DQRR_TOK_OFFSET, 8); ++ /* The user trying to poll for a result treats "dq" as const. It is ++ * however the same address that was provided to us non-const in the ++ * first place, for directing hardware DMA to. So we can cast away the ++ * const because it is mutable from our perspective. */ ++ uint32_t *p = qb_cl((struct dpaa2_dq *)dq); ++ uint32_t token; ++ ++ token = qb_attr_code_decode(&code_dqrr_tok_detect, &p[1]); ++ if (token != 1) ++ return 0; ++ qb_attr_code_encode(&code_dqrr_tok_detect, &p[1], 0); ++ ++ /* Only now do we convert from hardware to host endianness. Also, as we ++ * are returning success, the user has promised not to call us again, so ++ * there's no risk of us converting the endianness twice... */ ++ make_le32_n(p, 16); ++ ++ /* VDQCR "no longer busy" hook - not quite the same as DQRR, because the ++ * fact "VDQCR" shows busy doesn't mean that the result we're looking at ++ * is from the same command. Eg. we may be looking at our 10th dequeue ++ * result from our first VDQCR command, yet the second dequeue command ++ * could have been kicked off already, after seeing the 1st result. Ie. ++ * the result we're looking at is not necessarily proof that we can ++ * reset "busy". We instead base the decision on whether the current ++ * result is sitting at the first 'storage' location of the busy ++ * command. */ ++ if (s->vdq.storage == dq) { ++ s->vdq.storage = NULL; ++ atomic_inc(&s->vdq.busy); ++ } ++ return 1; ++} ++ ++/********************************/ ++/* Categorising qbman_result */ ++/********************************/ ++ ++static struct qb_attr_code code_result_in_mem = ++ QB_CODE(0, QBMAN_RESULT_VERB_OFFSET_IN_MEM, 7); ++ ++static inline int __qbman_result_is_x(const struct dpaa2_dq *dq, uint32_t x) ++{ ++ const uint32_t *p = qb_cl(dq); ++ uint32_t response_verb = qb_attr_code_decode(&code_dqrr_response, p); ++ ++ return response_verb == x; ++} ++ ++static inline int __qbman_result_is_x_in_mem(const struct dpaa2_dq *dq, ++ uint32_t x) ++{ ++ const uint32_t *p = qb_cl(dq); ++ uint32_t response_verb = qb_attr_code_decode(&code_result_in_mem, p); ++ ++ return (response_verb == x); ++} ++ ++int qbman_result_is_DQ(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x(dq, QBMAN_RESULT_DQ); ++} ++ ++int qbman_result_is_FQDAN(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x(dq, QBMAN_RESULT_FQDAN); ++} ++ ++int qbman_result_is_CDAN(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x(dq, QBMAN_RESULT_CDAN); ++} ++ ++int qbman_result_is_CSCN(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_CSCN_MEM) || ++ __qbman_result_is_x(dq, QBMAN_RESULT_CSCN_WQ); ++} ++ ++int qbman_result_is_BPSCN(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_BPSCN); ++} ++ ++int qbman_result_is_CGCU(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_CGCU); ++} ++ ++int qbman_result_is_FQRN(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_FQRN); ++} ++ ++int qbman_result_is_FQRNI(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x_in_mem(dq, QBMAN_RESULT_FQRNI); ++} ++ ++int qbman_result_is_FQPN(const struct dpaa2_dq *dq) ++{ ++ return __qbman_result_is_x(dq, QBMAN_RESULT_FQPN); ++} ++ ++/*********************************/ ++/* Parsing frame dequeue results */ ++/*********************************/ ++ ++/* These APIs assume qbman_result_is_DQ() is TRUE */ ++ ++uint32_t dpaa2_dq_flags(const struct dpaa2_dq *dq) ++{ ++ const uint32_t *p = qb_cl(dq); ++ ++ return qb_attr_code_decode(&code_dqrr_stat, p); ++} ++ ++uint16_t dpaa2_dq_seqnum(const struct dpaa2_dq *dq) ++{ ++ const uint32_t *p = qb_cl(dq); ++ ++ return (uint16_t)qb_attr_code_decode(&code_dqrr_seqnum, p); ++} ++ ++uint16_t dpaa2_dq_odpid(const struct dpaa2_dq *dq) ++{ ++ const uint32_t *p = qb_cl(dq); ++ ++ return (uint16_t)qb_attr_code_decode(&code_dqrr_odpid, p); ++} ++ ++uint32_t dpaa2_dq_fqid(const struct dpaa2_dq *dq) ++{ ++ const uint32_t *p = qb_cl(dq); ++ ++ return qb_attr_code_decode(&code_dqrr_fqid, p); ++} ++ ++uint32_t dpaa2_dq_byte_count(const struct dpaa2_dq *dq) ++{ ++ const uint32_t *p = qb_cl(dq); ++ ++ return qb_attr_code_decode(&code_dqrr_byte_count, p); ++} ++ ++uint32_t dpaa2_dq_frame_count(const struct dpaa2_dq *dq) ++{ ++ const uint32_t *p = qb_cl(dq); ++ ++ return qb_attr_code_decode(&code_dqrr_frame_count, p); ++} ++ ++uint64_t dpaa2_dq_fqd_ctx(const struct dpaa2_dq *dq) ++{ ++ const uint64_t *p = (uint64_t *)qb_cl(dq); ++ ++ return qb_attr_code_decode_64(&code_dqrr_ctx_lo, p); ++} ++EXPORT_SYMBOL(dpaa2_dq_fqd_ctx); ++ ++const struct dpaa2_fd *dpaa2_dq_fd(const struct dpaa2_dq *dq) ++{ ++ const uint32_t *p = qb_cl(dq); ++ ++ return (const struct dpaa2_fd *)&p[8]; ++} ++EXPORT_SYMBOL(dpaa2_dq_fd); ++ ++/**************************************/ ++/* Parsing state-change notifications */ ++/**************************************/ ++ ++static struct qb_attr_code code_scn_state = QB_CODE(0, 16, 8); ++static struct qb_attr_code code_scn_rid = QB_CODE(1, 0, 24); ++static struct qb_attr_code code_scn_state_in_mem = ++ QB_CODE(0, SCN_STATE_OFFSET_IN_MEM, 8); ++static struct qb_attr_code code_scn_rid_in_mem = ++ QB_CODE(1, SCN_RID_OFFSET_IN_MEM, 24); ++static struct qb_attr_code code_scn_ctx_lo = QB_CODE(2, 0, 32); ++ ++uint8_t qbman_result_SCN_state(const struct dpaa2_dq *scn) ++{ ++ const uint32_t *p = qb_cl(scn); ++ ++ return (uint8_t)qb_attr_code_decode(&code_scn_state, p); ++} ++ ++uint32_t qbman_result_SCN_rid(const struct dpaa2_dq *scn) ++{ ++ const uint32_t *p = qb_cl(scn); ++ ++ return qb_attr_code_decode(&code_scn_rid, p); ++} ++ ++uint64_t qbman_result_SCN_ctx(const struct dpaa2_dq *scn) ++{ ++ const uint64_t *p = (uint64_t *)qb_cl(scn); ++ ++ return qb_attr_code_decode_64(&code_scn_ctx_lo, p); ++} ++ ++uint8_t qbman_result_SCN_state_in_mem(const struct dpaa2_dq *scn) ++{ ++ const uint32_t *p = qb_cl(scn); ++ ++ return (uint8_t)qb_attr_code_decode(&code_scn_state_in_mem, p); ++} ++ ++uint32_t qbman_result_SCN_rid_in_mem(const struct dpaa2_dq *scn) ++{ ++ const uint32_t *p = qb_cl(scn); ++ uint32_t result_rid; ++ ++ result_rid = qb_attr_code_decode(&code_scn_rid_in_mem, p); ++ return make_le24(result_rid); ++} ++ ++/*****************/ ++/* Parsing BPSCN */ ++/*****************/ ++uint16_t qbman_result_bpscn_bpid(const struct dpaa2_dq *scn) ++{ ++ return (uint16_t)qbman_result_SCN_rid_in_mem(scn) & 0x3FFF; ++} ++ ++int qbman_result_bpscn_has_free_bufs(const struct dpaa2_dq *scn) ++{ ++ return !(int)(qbman_result_SCN_state_in_mem(scn) & 0x1); ++} ++ ++int qbman_result_bpscn_is_depleted(const struct dpaa2_dq *scn) ++{ ++ return (int)(qbman_result_SCN_state_in_mem(scn) & 0x2); ++} ++ ++int qbman_result_bpscn_is_surplus(const struct dpaa2_dq *scn) ++{ ++ return (int)(qbman_result_SCN_state_in_mem(scn) & 0x4); ++} ++ ++uint64_t qbman_result_bpscn_ctx(const struct dpaa2_dq *scn) ++{ ++ return qbman_result_SCN_ctx(scn); ++} ++ ++/*****************/ ++/* Parsing CGCU */ ++/*****************/ ++uint16_t qbman_result_cgcu_cgid(const struct dpaa2_dq *scn) ++{ ++ return (uint16_t)qbman_result_SCN_rid_in_mem(scn) & 0xFFFF; ++} ++ ++uint64_t qbman_result_cgcu_icnt(const struct dpaa2_dq *scn) ++{ ++ return qbman_result_SCN_ctx(scn) & 0xFFFFFFFFFF; ++} ++ ++/******************/ ++/* Buffer release */ ++/******************/ ++ ++/* These should be const, eventually */ ++/* static struct qb_attr_code code_release_num = QB_CODE(0, 0, 3); */ ++static struct qb_attr_code code_release_set_me = QB_CODE(0, 5, 1); ++static struct qb_attr_code code_release_rcdi = QB_CODE(0, 6, 1); ++static struct qb_attr_code code_release_bpid = QB_CODE(0, 16, 16); ++ ++void qbman_release_desc_clear(struct qbman_release_desc *d) ++{ ++ uint32_t *cl; ++ ++ memset(d, 0, sizeof(*d)); ++ cl = qb_cl(d); ++ qb_attr_code_encode(&code_release_set_me, cl, 1); ++} ++ ++void qbman_release_desc_set_bpid(struct qbman_release_desc *d, uint32_t bpid) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_release_bpid, cl, bpid); ++} ++ ++void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable) ++{ ++ uint32_t *cl = qb_cl(d); ++ ++ qb_attr_code_encode(&code_release_rcdi, cl, !!enable); ++} ++ ++#define RAR_IDX(rar) ((rar) & 0x7) ++#define RAR_VB(rar) ((rar) & 0x80) ++#define RAR_SUCCESS(rar) ((rar) & 0x100) ++ ++int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, ++ const uint64_t *buffers, unsigned int num_buffers) ++{ ++ uint32_t *p; ++ const uint32_t *cl = qb_cl(d); ++ uint32_t rar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_RAR); ++ ++ pr_debug("RAR=%08x\n", rar); ++ if (!RAR_SUCCESS(rar)) ++ return -EBUSY; ++ BUG_ON(!num_buffers || (num_buffers > 7)); ++ /* Start the release command */ ++ p = qbman_cena_write_start(&s->sys, ++ QBMAN_CENA_SWP_RCR(RAR_IDX(rar))); ++ /* Copy the caller's buffer pointers to the command */ ++ u64_to_le32_copy(&p[2], buffers, num_buffers); ++ /* Set the verb byte, have to substitute in the valid-bit and the number ++ * of buffers. */ ++ p[0] = cl[0] | RAR_VB(rar) | num_buffers; ++ qbman_cena_write_complete(&s->sys, ++ QBMAN_CENA_SWP_RCR(RAR_IDX(rar)), ++ p); ++ return 0; ++} ++ ++/*******************/ ++/* Buffer acquires */ ++/*******************/ ++ ++/* These should be const, eventually */ ++static struct qb_attr_code code_acquire_bpid = QB_CODE(0, 16, 16); ++static struct qb_attr_code code_acquire_num = QB_CODE(1, 0, 3); ++static struct qb_attr_code code_acquire_r_num = QB_CODE(1, 0, 3); ++ ++int qbman_swp_acquire(struct qbman_swp *s, uint32_t bpid, uint64_t *buffers, ++ unsigned int num_buffers) ++{ ++ uint32_t *p; ++ uint32_t verb, rslt, num; ++ ++ BUG_ON(!num_buffers || (num_buffers > 7)); ++ ++ /* Start the management command */ ++ p = qbman_swp_mc_start(s); ++ ++ if (!p) ++ return -EBUSY; ++ ++ /* Encode the caller-provided attributes */ ++ qb_attr_code_encode(&code_acquire_bpid, p, bpid); ++ qb_attr_code_encode(&code_acquire_num, p, num_buffers); ++ ++ /* Complete the management command */ ++ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_MC_ACQUIRE); ++ ++ /* Decode the outcome */ ++ verb = qb_attr_code_decode(&code_generic_verb, p); ++ rslt = qb_attr_code_decode(&code_generic_rslt, p); ++ num = qb_attr_code_decode(&code_acquire_r_num, p); ++ BUG_ON(verb != QBMAN_MC_ACQUIRE); ++ ++ /* Determine success or failure */ ++ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { ++ pr_err("Acquire buffers from BPID 0x%x failed, code=0x%02x\n", ++ bpid, rslt); ++ return -EIO; ++ } ++ BUG_ON(num > num_buffers); ++ /* Copy the acquired buffers to the caller's array */ ++ u64_from_le32_copy(buffers, &p[2], num); ++ return (int)num; ++} ++ ++/*****************/ ++/* FQ management */ ++/*****************/ ++ ++static struct qb_attr_code code_fqalt_fqid = QB_CODE(1, 0, 32); ++ ++static int qbman_swp_alt_fq_state(struct qbman_swp *s, uint32_t fqid, ++ uint8_t alt_fq_verb) ++{ ++ uint32_t *p; ++ uint32_t verb, rslt; ++ ++ /* Start the management command */ ++ p = qbman_swp_mc_start(s); ++ if (!p) ++ return -EBUSY; ++ ++ qb_attr_code_encode(&code_fqalt_fqid, p, fqid); ++ /* Complete the management command */ ++ p = qbman_swp_mc_complete(s, p, p[0] | alt_fq_verb); ++ ++ /* Decode the outcome */ ++ verb = qb_attr_code_decode(&code_generic_verb, p); ++ rslt = qb_attr_code_decode(&code_generic_rslt, p); ++ BUG_ON(verb != alt_fq_verb); ++ ++ /* Determine success or failure */ ++ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { ++ pr_err("ALT FQID %d failed: verb = 0x%08x, code = 0x%02x\n", ++ fqid, alt_fq_verb, rslt); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++int qbman_swp_fq_schedule(struct qbman_swp *s, uint32_t fqid) ++{ ++ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_SCHEDULE); ++} ++ ++int qbman_swp_fq_force(struct qbman_swp *s, uint32_t fqid) ++{ ++ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_FORCE); ++} ++ ++int qbman_swp_fq_xon(struct qbman_swp *s, uint32_t fqid) ++{ ++ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_XON); ++} ++ ++int qbman_swp_fq_xoff(struct qbman_swp *s, uint32_t fqid) ++{ ++ return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_XOFF); ++} ++ ++/**********************/ ++/* Channel management */ ++/**********************/ ++ ++static struct qb_attr_code code_cdan_cid = QB_CODE(0, 16, 12); ++static struct qb_attr_code code_cdan_we = QB_CODE(1, 0, 8); ++static struct qb_attr_code code_cdan_en = QB_CODE(1, 8, 1); ++static struct qb_attr_code code_cdan_ctx_lo = QB_CODE(2, 0, 32); ++ ++/* Hide "ICD" for now as we don't use it, don't set it, and don't test it, so it ++ * would be irresponsible to expose it. */ ++#define CODE_CDAN_WE_EN 0x1 ++#define CODE_CDAN_WE_CTX 0x4 ++ ++static int qbman_swp_CDAN_set(struct qbman_swp *s, uint16_t channelid, ++ uint8_t we_mask, uint8_t cdan_en, ++ uint64_t ctx) ++{ ++ uint32_t *p; ++ uint32_t verb, rslt; ++ ++ /* Start the management command */ ++ p = qbman_swp_mc_start(s); ++ if (!p) ++ return -EBUSY; ++ ++ /* Encode the caller-provided attributes */ ++ qb_attr_code_encode(&code_cdan_cid, p, channelid); ++ qb_attr_code_encode(&code_cdan_we, p, we_mask); ++ qb_attr_code_encode(&code_cdan_en, p, cdan_en); ++ qb_attr_code_encode_64(&code_cdan_ctx_lo, (uint64_t *)p, ctx); ++ /* Complete the management command */ ++ p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_WQCHAN_CONFIGURE); ++ ++ /* Decode the outcome */ ++ verb = qb_attr_code_decode(&code_generic_verb, p); ++ rslt = qb_attr_code_decode(&code_generic_rslt, p); ++ BUG_ON(verb != QBMAN_WQCHAN_CONFIGURE); ++ ++ /* Determine success or failure */ ++ if (unlikely(rslt != QBMAN_MC_RSLT_OK)) { ++ pr_err("CDAN cQID %d failed: code = 0x%02x\n", ++ channelid, rslt); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++int qbman_swp_CDAN_set_context(struct qbman_swp *s, uint16_t channelid, ++ uint64_t ctx) ++{ ++ return qbman_swp_CDAN_set(s, channelid, ++ CODE_CDAN_WE_CTX, ++ 0, ctx); ++} ++ ++int qbman_swp_CDAN_enable(struct qbman_swp *s, uint16_t channelid) ++{ ++ return qbman_swp_CDAN_set(s, channelid, ++ CODE_CDAN_WE_EN, ++ 1, 0); ++} ++int qbman_swp_CDAN_disable(struct qbman_swp *s, uint16_t channelid) ++{ ++ return qbman_swp_CDAN_set(s, channelid, ++ CODE_CDAN_WE_EN, ++ 0, 0); ++} ++int qbman_swp_CDAN_set_context_enable(struct qbman_swp *s, uint16_t channelid, ++ uint64_t ctx) ++{ ++ return qbman_swp_CDAN_set(s, channelid, ++ CODE_CDAN_WE_EN | CODE_CDAN_WE_CTX, ++ 1, ctx); ++} +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/qbman_portal.h +@@ -0,0 +1,261 @@ ++/* Copyright (C) 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. ++ */ ++ ++#include "qbman_private.h" ++#include "fsl_qbman_portal.h" ++#include "../../include/fsl_dpaa2_fd.h" ++ ++/* All QBMan command and result structures use this "valid bit" encoding */ ++#define QB_VALID_BIT ((uint32_t)0x80) ++ ++/* Management command result codes */ ++#define QBMAN_MC_RSLT_OK 0xf0 ++ ++/* TBD: as of QBMan 4.1, DQRR will be 8 rather than 4! */ ++#define QBMAN_DQRR_SIZE 4 ++ ++/* DQRR valid-bit reset bug. See qbman_portal.c::qbman_swp_init(). */ ++#define WORKAROUND_DQRR_RESET_BUG ++ ++/* --------------------- */ ++/* portal data structure */ ++/* --------------------- */ ++ ++struct qbman_swp { ++ const struct qbman_swp_desc *desc; ++ /* The qbman_sys (ie. arch/OS-specific) support code can put anything it ++ * needs in here. */ ++ struct qbman_swp_sys sys; ++ /* Management commands */ ++ struct { ++#ifdef QBMAN_CHECKING ++ enum swp_mc_check { ++ swp_mc_can_start, /* call __qbman_swp_mc_start() */ ++ swp_mc_can_submit, /* call __qbman_swp_mc_submit() */ ++ swp_mc_can_poll, /* call __qbman_swp_mc_result() */ ++ } check; ++#endif ++ uint32_t valid_bit; /* 0x00 or 0x80 */ ++ } mc; ++ /* Push dequeues */ ++ uint32_t sdq; ++ /* Volatile dequeues */ ++ struct { ++ /* VDQCR supports a "1 deep pipeline", meaning that if you know ++ * the last-submitted command is already executing in the ++ * hardware (as evidenced by at least 1 valid dequeue result), ++ * you can write another dequeue command to the register, the ++ * hardware will start executing it as soon as the ++ * already-executing command terminates. (This minimises latency ++ * and stalls.) With that in mind, this "busy" variable refers ++ * to whether or not a command can be submitted, not whether or ++ * not a previously-submitted command is still executing. In ++ * other words, once proof is seen that the previously-submitted ++ * command is executing, "vdq" is no longer "busy". ++ */ ++ atomic_t busy; ++ uint32_t valid_bit; /* 0x00 or 0x80 */ ++ /* We need to determine when vdq is no longer busy. This depends ++ * on whether the "busy" (last-submitted) dequeue command is ++ * targeting DQRR or main-memory, and detected is based on the ++ * presence of the dequeue command's "token" showing up in ++ * dequeue entries in DQRR or main-memory (respectively). */ ++ struct dpaa2_dq *storage; /* NULL if DQRR */ ++ } vdq; ++ /* DQRR */ ++ struct { ++ uint32_t next_idx; ++ uint32_t valid_bit; ++ uint8_t dqrr_size; ++#ifdef WORKAROUND_DQRR_RESET_BUG ++ int reset_bug; ++#endif ++ } dqrr; ++}; ++ ++/* -------------------------- */ ++/* portal management commands */ ++/* -------------------------- */ ++ ++/* Different management commands all use this common base layer of code to issue ++ * commands and poll for results. The first function returns a pointer to where ++ * the caller should fill in their MC command (though they should ignore the ++ * verb byte), the second function commits merges in the caller-supplied command ++ * verb (which should not include the valid-bit) and submits the command to ++ * hardware, and the third function checks for a completed response (returns ++ * non-NULL if only if the response is complete). */ ++void *qbman_swp_mc_start(struct qbman_swp *p); ++void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint32_t cmd_verb); ++void *qbman_swp_mc_result(struct qbman_swp *p); ++ ++/* Wraps up submit + poll-for-result */ ++static inline void *qbman_swp_mc_complete(struct qbman_swp *swp, void *cmd, ++ uint32_t cmd_verb) ++{ ++ int loopvar; ++ ++ qbman_swp_mc_submit(swp, cmd, cmd_verb); ++ DBG_POLL_START(loopvar); ++ do { ++ DBG_POLL_CHECK(loopvar); ++ cmd = qbman_swp_mc_result(swp); ++ } while (!cmd); ++ return cmd; ++} ++ ++/* ------------ */ ++/* qb_attr_code */ ++/* ------------ */ ++ ++/* This struct locates a sub-field within a QBMan portal (CENA) cacheline which ++ * is either serving as a configuration command or a query result. The ++ * representation is inherently little-endian, as the indexing of the words is ++ * itself little-endian in nature and layerscape is little endian for anything ++ * that crosses a word boundary too (64-bit fields are the obvious examples). ++ */ ++struct qb_attr_code { ++ unsigned int word; /* which uint32_t[] array member encodes the field */ ++ unsigned int lsoffset; /* encoding offset from ls-bit */ ++ unsigned int width; /* encoding width. (bool must be 1.) */ ++}; ++ ++/* Some pre-defined codes */ ++extern struct qb_attr_code code_generic_verb; ++extern struct qb_attr_code code_generic_rslt; ++ ++/* Macros to define codes */ ++#define QB_CODE(a, b, c) { a, b, c} ++#define QB_CODE_NULL \ ++ QB_CODE((unsigned int)-1, (unsigned int)-1, (unsigned int)-1) ++ ++/* Rotate a code "ms", meaning that it moves from less-significant bytes to ++ * more-significant, from less-significant words to more-significant, etc. The ++ * "ls" version does the inverse, from more-significant towards ++ * less-significant. ++ */ ++static inline void qb_attr_code_rotate_ms(struct qb_attr_code *code, ++ unsigned int bits) ++{ ++ code->lsoffset += bits; ++ while (code->lsoffset > 31) { ++ code->word++; ++ code->lsoffset -= 32; ++ } ++} ++static inline void qb_attr_code_rotate_ls(struct qb_attr_code *code, ++ unsigned int bits) ++{ ++ /* Don't be fooled, this trick should work because the types are ++ * unsigned. So the case that interests the while loop (the rotate has ++ * gone too far and the word count needs to compensate for it), is ++ * manifested when lsoffset is negative. But that equates to a really ++ * large unsigned value, starting with lots of "F"s. As such, we can ++ * continue adding 32 back to it until it wraps back round above zero, ++ * to a value of 31 or less... ++ */ ++ code->lsoffset -= bits; ++ while (code->lsoffset > 31) { ++ code->word--; ++ code->lsoffset += 32; ++ } ++} ++/* Implement a loop of code rotations until 'expr' evaluates to FALSE (0). */ ++#define qb_attr_code_for_ms(code, bits, expr) \ ++ for (; expr; qb_attr_code_rotate_ms(code, bits)) ++#define qb_attr_code_for_ls(code, bits, expr) \ ++ for (; expr; qb_attr_code_rotate_ls(code, bits)) ++ ++/* decode a field from a cacheline */ ++static inline uint32_t qb_attr_code_decode(const struct qb_attr_code *code, ++ const uint32_t *cacheline) ++{ ++ return d32_uint32_t(code->lsoffset, code->width, cacheline[code->word]); ++} ++static inline uint64_t qb_attr_code_decode_64(const struct qb_attr_code *code, ++ const uint64_t *cacheline) ++{ ++ uint64_t res; ++ u64_from_le32_copy(&res, &cacheline[code->word/2], 1); ++ return res; ++} ++ ++/* encode a field to a cacheline */ ++static inline void qb_attr_code_encode(const struct qb_attr_code *code, ++ uint32_t *cacheline, uint32_t val) ++{ ++ cacheline[code->word] = ++ r32_uint32_t(code->lsoffset, code->width, cacheline[code->word]) ++ | e32_uint32_t(code->lsoffset, code->width, val); ++} ++static inline void qb_attr_code_encode_64(const struct qb_attr_code *code, ++ uint64_t *cacheline, uint64_t val) ++{ ++ u64_to_le32_copy(&cacheline[code->word/2], &val, 1); ++} ++ ++/* Small-width signed values (two's-complement) will decode into medium-width ++ * positives. (Eg. for an 8-bit signed field, which stores values from -128 to ++ * +127, a setting of -7 would appear to decode to the 32-bit unsigned value ++ * 249. Likewise -120 would decode as 136.) This function allows the caller to ++ * "re-sign" such fields to 32-bit signed. (Eg. -7, which was 249 with an 8-bit ++ * encoding, will become 0xfffffff9 if you cast the return value to uint32_t). ++ */ ++static inline int32_t qb_attr_code_makesigned(const struct qb_attr_code *code, ++ uint32_t val) ++{ ++ BUG_ON(val >= (1 << code->width)); ++ /* If the high bit was set, it was encoding a negative */ ++ if (val >= (1 << (code->width - 1))) ++ return (int32_t)0 - (int32_t)(((uint32_t)1 << code->width) - ++ val); ++ /* Otherwise, it was encoding a positive */ ++ return (int32_t)val; ++} ++ ++/* ---------------------- */ ++/* Descriptors/cachelines */ ++/* ---------------------- */ ++ ++/* To avoid needless dynamic allocation, the driver API often gives the caller ++ * a "descriptor" type that the caller can instantiate however they like. ++ * Ultimately though, it is just a cacheline of binary storage (or something ++ * smaller when it is known that the descriptor doesn't need all 64 bytes) for ++ * holding pre-formatted pieces of hardware commands. The performance-critical ++ * code can then copy these descriptors directly into hardware command ++ * registers more efficiently than trying to construct/format commands ++ * on-the-fly. The API user sees the descriptor as an array of 32-bit words in ++ * order for the compiler to know its size, but the internal details are not ++ * exposed. The following macro is used within the driver for converting *any* ++ * descriptor pointer to a usable array pointer. The use of a macro (instead of ++ * an inline) is necessary to work with different descriptor types and to work ++ * correctly with const and non-const inputs (and similarly-qualified outputs). ++ */ ++#define qb_cl(d) (&(d)->dont_manipulate_directly[0]) +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/qbman_private.h +@@ -0,0 +1,173 @@ ++/* Copyright (C) 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. ++*/ ++ ++/* Perform extra checking */ ++#define QBMAN_CHECKING ++ ++/* To maximise the amount of logic that is common between the Linux driver and ++ * other targets (such as the embedded MC firmware), we pivot here between the ++ * inclusion of two platform-specific headers. ++ * ++ * The first, qbman_sys_decl.h, includes any and all required system headers as ++ * well as providing any definitions for the purposes of compatibility. The ++ * second, qbman_sys.h, is where platform-specific routines go. ++ * ++ * The point of the split is that the platform-independent code (including this ++ * header) may depend on platform-specific declarations, yet other ++ * platform-specific routines may depend on platform-independent definitions. ++ */ ++ ++#include "qbman_sys_decl.h" ++ ++#define QMAN_REV_4000 0x04000000 ++#define QMAN_REV_4100 0x04010000 ++#define QMAN_REV_4101 0x04010001 ++ ++/* When things go wrong, it is a convenient trick to insert a few FOO() ++ * statements in the code to trace progress. TODO: remove this once we are ++ * hacking the code less actively. ++ */ ++#define FOO() fsl_os_print("FOO: %s:%d\n", __FILE__, __LINE__) ++ ++/* Any time there is a register interface which we poll on, this provides a ++ * "break after x iterations" scheme for it. It's handy for debugging, eg. ++ * where you don't want millions of lines of log output from a polling loop ++ * that won't, because such things tend to drown out the earlier log output ++ * that might explain what caused the problem. (NB: put ";" after each macro!) ++ * TODO: we should probably remove this once we're done sanitising the ++ * simulator... ++ */ ++#define DBG_POLL_START(loopvar) (loopvar = 10) ++#define DBG_POLL_CHECK(loopvar) \ ++ do {if (!(loopvar--)) BUG_ON(1); } while (0) ++ ++/* For CCSR or portal-CINH registers that contain fields at arbitrary offsets ++ * and widths, these macro-generated encode/decode/isolate/remove inlines can ++ * be used. ++ * ++ * Eg. to "d"ecode a 14-bit field out of a register (into a "uint16_t" type), ++ * where the field is located 3 bits "up" from the least-significant bit of the ++ * register (ie. the field location within the 32-bit register corresponds to a ++ * mask of 0x0001fff8), you would do; ++ * uint16_t field = d32_uint16_t(3, 14, reg_value); ++ * ++ * Or to "e"ncode a 1-bit boolean value (input type is "int", zero is FALSE, ++ * non-zero is TRUE, so must convert all non-zero inputs to 1, hence the "!!" ++ * operator) into a register at bit location 0x00080000 (19 bits "in" from the ++ * LS bit), do; ++ * reg_value |= e32_int(19, 1, !!field); ++ * ++ * If you wish to read-modify-write a register, such that you leave the 14-bit ++ * field as-is but have all other fields set to zero, then "i"solate the 14-bit ++ * value using; ++ * reg_value = i32_uint16_t(3, 14, reg_value); ++ * ++ * Alternatively, you could "r"emove the 1-bit boolean field (setting it to ++ * zero) but leaving all other fields as-is; ++ * reg_val = r32_int(19, 1, reg_value); ++ * ++ */ ++#define MAKE_MASK32(width) (width == 32 ? 0xffffffff : \ ++ (uint32_t)((1 << width) - 1)) ++#define DECLARE_CODEC32(t) \ ++static inline uint32_t e32_##t(uint32_t lsoffset, uint32_t width, t val) \ ++{ \ ++ BUG_ON(width > (sizeof(t) * 8)); \ ++ return ((uint32_t)val & MAKE_MASK32(width)) << lsoffset; \ ++} \ ++static inline t d32_##t(uint32_t lsoffset, uint32_t width, uint32_t val) \ ++{ \ ++ BUG_ON(width > (sizeof(t) * 8)); \ ++ return (t)((val >> lsoffset) & MAKE_MASK32(width)); \ ++} \ ++static inline uint32_t i32_##t(uint32_t lsoffset, uint32_t width, \ ++ uint32_t val) \ ++{ \ ++ BUG_ON(width > (sizeof(t) * 8)); \ ++ return e32_##t(lsoffset, width, d32_##t(lsoffset, width, val)); \ ++} \ ++static inline uint32_t r32_##t(uint32_t lsoffset, uint32_t width, \ ++ uint32_t val) \ ++{ \ ++ BUG_ON(width > (sizeof(t) * 8)); \ ++ return ~(MAKE_MASK32(width) << lsoffset) & val; \ ++} ++DECLARE_CODEC32(uint32_t) ++DECLARE_CODEC32(uint16_t) ++DECLARE_CODEC32(uint8_t) ++DECLARE_CODEC32(int) ++ ++ /*********************/ ++ /* Debugging assists */ ++ /*********************/ ++ ++static inline void __hexdump(unsigned long start, unsigned long end, ++ unsigned long p, size_t sz, const unsigned char *c) ++{ ++ while (start < end) { ++ unsigned int pos = 0; ++ char buf[64]; ++ int nl = 0; ++ ++ pos += sprintf(buf + pos, "%08lx: ", start); ++ do { ++ if ((start < p) || (start >= (p + sz))) ++ pos += sprintf(buf + pos, ".."); ++ else ++ pos += sprintf(buf + pos, "%02x", *(c++)); ++ if (!(++start & 15)) { ++ buf[pos++] = '\n'; ++ nl = 1; ++ } else { ++ nl = 0; ++ if (!(start & 1)) ++ buf[pos++] = ' '; ++ if (!(start & 3)) ++ buf[pos++] = ' '; ++ } ++ } while (start & 15); ++ if (!nl) ++ buf[pos++] = '\n'; ++ buf[pos] = '\0'; ++ pr_info("%s", buf); ++ } ++} ++static inline void hexdump(const void *ptr, size_t sz) ++{ ++ unsigned long p = (unsigned long)ptr; ++ unsigned long start = p & ~(unsigned long)15; ++ unsigned long end = (p + sz + 15) & ~(unsigned long)15; ++ const unsigned char *c = ptr; ++ ++ __hexdump(start, end, p, sz, c); ++} ++ ++#include "qbman_sys.h" +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/qbman_sys.h +@@ -0,0 +1,307 @@ ++/* Copyright (C) 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. ++ */ ++/* qbman_sys_decl.h and qbman_sys.h are the two platform-specific files in the ++ * driver. They are only included via qbman_private.h, which is itself a ++ * platform-independent file and is included by all the other driver source. ++ * ++ * qbman_sys_decl.h is included prior to all other declarations and logic, and ++ * it exists to provide compatibility with any linux interfaces our ++ * single-source driver code is dependent on (eg. kmalloc). Ie. this file ++ * provides linux compatibility. ++ * ++ * This qbman_sys.h header, on the other hand, is included *after* any common ++ * and platform-neutral declarations and logic in qbman_private.h, and exists to ++ * implement any platform-specific logic of the qbman driver itself. Ie. it is ++ * *not* to provide linux compatibility. ++ */ ++ ++/* Trace the 3 different classes of read/write access to QBMan. #undef as ++ * required. */ ++#undef QBMAN_CCSR_TRACE ++#undef QBMAN_CINH_TRACE ++#undef QBMAN_CENA_TRACE ++ ++static inline void word_copy(void *d, const void *s, unsigned int cnt) ++{ ++ uint32_t *dd = d; ++ const uint32_t *ss = s; ++ ++ while (cnt--) ++ *(dd++) = *(ss++); ++} ++ ++/* Currently, the CENA support code expects each 32-bit word to be written in ++ * host order, and these are converted to hardware (little-endian) order on ++ * command submission. However, 64-bit quantities are must be written (and read) ++ * as two 32-bit words with the least-significant word first, irrespective of ++ * host endianness. */ ++static inline void u64_to_le32_copy(void *d, const uint64_t *s, ++ unsigned int cnt) ++{ ++ uint32_t *dd = d; ++ const uint32_t *ss = (const uint32_t *)s; ++ ++ while (cnt--) { ++ /* TBD: the toolchain was choking on the use of 64-bit types up ++ * until recently so this works entirely with 32-bit variables. ++ * When 64-bit types become usable again, investigate better ++ * ways of doing this. */ ++#if defined(__BIG_ENDIAN) ++ *(dd++) = ss[1]; ++ *(dd++) = ss[0]; ++ ss += 2; ++#else ++ *(dd++) = *(ss++); ++ *(dd++) = *(ss++); ++#endif ++ } ++} ++static inline void u64_from_le32_copy(uint64_t *d, const void *s, ++ unsigned int cnt) ++{ ++ const uint32_t *ss = s; ++ uint32_t *dd = (uint32_t *)d; ++ ++ while (cnt--) { ++#if defined(__BIG_ENDIAN) ++ dd[1] = *(ss++); ++ dd[0] = *(ss++); ++ dd += 2; ++#else ++ *(dd++) = *(ss++); ++ *(dd++) = *(ss++); ++#endif ++ } ++} ++ ++/* Convert a host-native 32bit value into little endian */ ++#if defined(__BIG_ENDIAN) ++static inline uint32_t make_le32(uint32_t val) ++{ ++ return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ++ ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24); ++} ++static inline uint32_t make_le24(uint32_t val) ++{ ++ return (((val & 0xff) << 16) | (val & 0xff00) | ++ ((val & 0xff0000) >> 16)); ++} ++#else ++#define make_le32(val) (val) ++#define make_le24(val) (val) ++#endif ++static inline void make_le32_n(uint32_t *val, unsigned int num) ++{ ++ while (num--) { ++ *val = make_le32(*val); ++ val++; ++ } ++} ++ ++ /******************/ ++ /* Portal access */ ++ /******************/ ++struct qbman_swp_sys { ++ /* On GPP, the sys support for qbman_swp is here. The CENA region isi ++ * not an mmap() of the real portal registers, but an allocated ++ * place-holder, because the actual writes/reads to/from the portal are ++ * marshalled from these allocated areas using QBMan's "MC access ++ * registers". CINH accesses are atomic so there's no need for a ++ * place-holder. */ ++ void *cena; ++ void __iomem *addr_cena; ++ void __iomem *addr_cinh; ++}; ++ ++/* P_OFFSET is (ACCESS_CMD,0,12) - offset within the portal ++ * C is (ACCESS_CMD,12,1) - is inhibited? (0==CENA, 1==CINH) ++ * SWP_IDX is (ACCESS_CMD,16,10) - Software portal index ++ * P is (ACCESS_CMD,28,1) - (0==special portal, 1==any portal) ++ * T is (ACCESS_CMD,29,1) - Command type (0==READ, 1==WRITE) ++ * E is (ACCESS_CMD,31,1) - Command execute (1 to issue, poll for 0==complete) ++ */ ++ ++static inline void qbman_cinh_write(struct qbman_swp_sys *s, uint32_t offset, ++ uint32_t val) ++{ ++ ++ writel_relaxed(val, s->addr_cinh + offset); ++#ifdef QBMAN_CINH_TRACE ++ pr_info("qbman_cinh_write(%p:0x%03x) 0x%08x\n", ++ s->addr_cinh, offset, val); ++#endif ++} ++ ++static inline uint32_t qbman_cinh_read(struct qbman_swp_sys *s, uint32_t offset) ++{ ++ uint32_t reg = readl_relaxed(s->addr_cinh + offset); ++ ++#ifdef QBMAN_CINH_TRACE ++ pr_info("qbman_cinh_read(%p:0x%03x) 0x%08x\n", ++ s->addr_cinh, offset, reg); ++#endif ++ return reg; ++} ++ ++static inline void *qbman_cena_write_start(struct qbman_swp_sys *s, ++ uint32_t offset) ++{ ++ void *shadow = s->cena + offset; ++ ++#ifdef QBMAN_CENA_TRACE ++ pr_info("qbman_cena_write_start(%p:0x%03x) %p\n", ++ s->addr_cena, offset, shadow); ++#endif ++ BUG_ON(offset & 63); ++ dcbz(shadow); ++ return shadow; ++} ++ ++static inline void qbman_cena_write_complete(struct qbman_swp_sys *s, ++ uint32_t offset, void *cmd) ++{ ++ const uint32_t *shadow = cmd; ++ int loop; ++ ++#ifdef QBMAN_CENA_TRACE ++ pr_info("qbman_cena_write_complete(%p:0x%03x) %p\n", ++ s->addr_cena, offset, shadow); ++ hexdump(cmd, 64); ++#endif ++ for (loop = 15; loop >= 1; loop--) ++ writel_relaxed(shadow[loop], s->addr_cena + ++ offset + loop * 4); ++ lwsync(); ++ writel_relaxed(shadow[0], s->addr_cena + offset); ++ dcbf(s->addr_cena + offset); ++} ++ ++static inline void *qbman_cena_read(struct qbman_swp_sys *s, uint32_t offset) ++{ ++ uint32_t *shadow = s->cena + offset; ++ unsigned int loop; ++ ++#ifdef QBMAN_CENA_TRACE ++ pr_info("qbman_cena_read(%p:0x%03x) %p\n", ++ s->addr_cena, offset, shadow); ++#endif ++ ++ for (loop = 0; loop < 16; loop++) ++ shadow[loop] = readl_relaxed(s->addr_cena + offset ++ + loop * 4); ++#ifdef QBMAN_CENA_TRACE ++ hexdump(shadow, 64); ++#endif ++ return shadow; ++} ++ ++static inline void qbman_cena_invalidate_prefetch(struct qbman_swp_sys *s, ++ uint32_t offset) ++{ ++ dcivac(s->addr_cena + offset); ++ prefetch_for_load(s->addr_cena + offset); ++} ++ ++ /******************/ ++ /* Portal support */ ++ /******************/ ++ ++/* The SWP_CFG portal register is special, in that it is used by the ++ * platform-specific code rather than the platform-independent code in ++ * qbman_portal.c. So use of it is declared locally here. */ ++#define QBMAN_CINH_SWP_CFG 0xd00 ++ ++/* For MC portal use, we always configure with ++ * DQRR_MF is (SWP_CFG,20,3) - DQRR max fill (<- 0x4) ++ * EST is (SWP_CFG,16,3) - EQCR_CI stashing threshold (<- 0x0) ++ * RPM is (SWP_CFG,12,2) - RCR production notification mode (<- 0x3) ++ * DCM is (SWP_CFG,10,2) - DQRR consumption notification mode (<- 0x2) ++ * EPM is (SWP_CFG,8,2) - EQCR production notification mode (<- 0x3) ++ * SD is (SWP_CFG,5,1) - memory stashing drop enable (<- FALSE) ++ * SP is (SWP_CFG,4,1) - memory stashing priority (<- TRUE) ++ * SE is (SWP_CFG,3,1) - memory stashing enable (<- 0x0) ++ * DP is (SWP_CFG,2,1) - dequeue stashing priority (<- TRUE) ++ * DE is (SWP_CFG,1,1) - dequeue stashing enable (<- 0x0) ++ * EP is (SWP_CFG,0,1) - EQCR_CI stashing priority (<- FALSE) ++ */ ++static inline uint32_t qbman_set_swp_cfg(uint8_t max_fill, uint8_t wn, ++ uint8_t est, uint8_t rpm, uint8_t dcm, ++ uint8_t epm, int sd, int sp, int se, ++ int dp, int de, int ep) ++{ ++ uint32_t reg; ++ ++ reg = e32_uint8_t(20, (uint32_t)(3 + (max_fill >> 3)), max_fill) | ++ e32_uint8_t(16, 3, est) | e32_uint8_t(12, 2, rpm) | ++ e32_uint8_t(10, 2, dcm) | e32_uint8_t(8, 2, epm) | ++ e32_int(5, 1, sd) | e32_int(4, 1, sp) | e32_int(3, 1, se) | ++ e32_int(2, 1, dp) | e32_int(1, 1, de) | e32_int(0, 1, ep) | ++ e32_uint8_t(14, 1, wn); ++ return reg; ++} ++ ++static inline int qbman_swp_sys_init(struct qbman_swp_sys *s, ++ const struct qbman_swp_desc *d, ++ uint8_t dqrr_size) ++{ ++ uint32_t reg; ++ ++ s->addr_cena = d->cena_bar; ++ s->addr_cinh = d->cinh_bar; ++ s->cena = (void *)get_zeroed_page(GFP_KERNEL); ++ if (!s->cena) { ++ pr_err("Could not allocate page for cena shadow\n"); ++ return -1; ++ } ++ ++#ifdef QBMAN_CHECKING ++ /* We should never be asked to initialise for a portal that isn't in ++ * the power-on state. (Ie. don't forget to reset portals when they are ++ * decommissioned!) ++ */ ++ reg = qbman_cinh_read(s, QBMAN_CINH_SWP_CFG); ++ BUG_ON(reg); ++#endif ++ reg = qbman_set_swp_cfg(dqrr_size, 0, 0, 3, 2, 3, 0, 1, 0, 1, 0, 0); ++ qbman_cinh_write(s, QBMAN_CINH_SWP_CFG, reg); ++ reg = qbman_cinh_read(s, QBMAN_CINH_SWP_CFG); ++ if (!reg) { ++ pr_err("The portal is not enabled!\n"); ++ kfree(s->cena); ++ return -1; ++ } ++ return 0; ++} ++ ++static inline void qbman_swp_sys_finish(struct qbman_swp_sys *s) ++{ ++ free_page((unsigned long)s->cena); ++} +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/qbman_sys_decl.h +@@ -0,0 +1,86 @@ ++/* Copyright (C) 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. ++ */ ++#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/memblock.h> ++#include <linux/completion.h> ++#include <linux/log2.h> ++#include <linux/types.h> ++#include <linux/ioctl.h> ++#include <linux/device.h> ++#include <linux/smp.h> ++#include <linux/vmalloc.h> ++#include "fsl_qbman_base.h" ++ ++/* The platform-independent code shouldn't need endianness, except for ++ * weird/fast-path cases like qbman_result_has_token(), which needs to ++ * perform a passive and endianness-specific test on a read-only data structure ++ * very quickly. It's an exception, and this symbol is used for that case. */ ++#if defined(__BIG_ENDIAN) ++#define DQRR_TOK_OFFSET 0 ++#define QBMAN_RESULT_VERB_OFFSET_IN_MEM 24 ++#define SCN_STATE_OFFSET_IN_MEM 8 ++#define SCN_RID_OFFSET_IN_MEM 8 ++#else ++#define DQRR_TOK_OFFSET 24 ++#define QBMAN_RESULT_VERB_OFFSET_IN_MEM 0 ++#define SCN_STATE_OFFSET_IN_MEM 16 ++#define SCN_RID_OFFSET_IN_MEM 0 ++#endif ++ ++/* Similarly-named functions */ ++#define upper32(a) upper_32_bits(a) ++#define lower32(a) lower_32_bits(a) ++ ++ /****************/ ++ /* arch assists */ ++ /****************/ ++ ++#define dcbz(p) { asm volatile("dc zva, %0" : : "r" (p) : "memory"); } ++#define lwsync() { asm volatile("dmb st" : : : "memory"); } ++#define dcbf(p) { asm volatile("dc cvac, %0;" : : "r" (p) : "memory"); } ++#define dcivac(p) { asm volatile("dc ivac, %0" : : "r"(p) : "memory"); } ++static inline void prefetch_for_load(void *p) ++{ ++ asm volatile("prfm pldl1keep, [%0, #64]" : : "r" (p)); ++} ++static inline void prefetch_for_store(void *p) ++{ ++ asm volatile("prfm pstl1keep, [%0, #64]" : : "r" (p)); ++} +--- /dev/null ++++ b/drivers/staging/fsl-mc/bus/dpio/qbman_test.c +@@ -0,0 +1,664 @@ ++/* Copyright (C) 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. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/io.h> ++#include <linux/module.h> ++ ++#include "qbman_private.h" ++#include "fsl_qbman_portal.h" ++#include "qbman_debug.h" ++#include "../../include/fsl_dpaa2_fd.h" ++ ++#define QBMAN_SWP_CENA_BASE 0x818000000 ++#define QBMAN_SWP_CINH_BASE 0x81c000000 ++ ++#define QBMAN_PORTAL_IDX 2 ++#define QBMAN_TEST_FQID 19 ++#define QBMAN_TEST_BPID 23 ++#define QBMAN_USE_QD ++#ifdef QBMAN_USE_QD ++#define QBMAN_TEST_QDID 1 ++#endif ++#define QBMAN_TEST_LFQID 0xf00010 ++ ++#define NUM_EQ_FRAME 10 ++#define NUM_DQ_FRAME 10 ++#define NUM_DQ_IN_DQRR 5 ++#define NUM_DQ_IN_MEM (NUM_DQ_FRAME - NUM_DQ_IN_DQRR) ++ ++static struct qbman_swp *swp; ++static struct qbman_eq_desc eqdesc; ++static struct qbman_pull_desc pulldesc; ++static struct qbman_release_desc releasedesc; ++static struct qbman_eq_response eq_storage[1]; ++static struct dpaa2_dq dq_storage[NUM_DQ_IN_MEM] __aligned(64); ++static dma_addr_t eq_storage_phys; ++static dma_addr_t dq_storage_phys; ++ ++/* FQ ctx attribute values for the test code. */ ++#define FQCTX_HI 0xabbaf00d ++#define FQCTX_LO 0x98765432 ++#define FQ_VFQID 0x123456 ++ ++/* Sample frame descriptor */ ++static struct qbman_fd_simple fd = { ++ .addr_lo = 0xbabaf33d, ++ .addr_hi = 0x01234567, ++ .len = 0x7777, ++ .frc = 0xdeadbeef, ++ .flc_lo = 0xcafecafe, ++ .flc_hi = 0xbeadabba ++}; ++ ++static void fd_inc(struct qbman_fd_simple *_fd) ++{ ++ _fd->addr_lo += _fd->len; ++ _fd->flc_lo += 0x100; ++ _fd->frc += 0x10; ++} ++ ++static int fd_cmp(struct qbman_fd *fda, struct qbman_fd *fdb) ++{ ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ if (fda->words[i] - fdb->words[i]) ++ return 1; ++ return 0; ++} ++ ++struct qbman_fd fd_eq[NUM_EQ_FRAME]; ++struct qbman_fd fd_dq[NUM_DQ_FRAME]; ++ ++/* "Buffers" to be released (and storage for buffers to be acquired) */ ++static uint64_t rbufs[320]; ++static uint64_t abufs[320]; ++ ++static void do_enqueue(struct qbman_swp *swp) ++{ ++ int i, j, ret; ++ ++#ifdef QBMAN_USE_QD ++ pr_info("*****QBMan_test: Enqueue %d frames to QD %d\n", ++ NUM_EQ_FRAME, QBMAN_TEST_QDID); ++#else ++ pr_info("*****QBMan_test: Enqueue %d frames to FQ %d\n", ++ NUM_EQ_FRAME, QBMAN_TEST_FQID); ++#endif ++ for (i = 0; i < NUM_EQ_FRAME; i++) { ++ /*********************************/ ++ /* Prepare a enqueue descriptor */ ++ /*********************************/ ++ memset(eq_storage, 0, sizeof(eq_storage)); ++ eq_storage_phys = virt_to_phys(eq_storage); ++ qbman_eq_desc_clear(&eqdesc); ++ qbman_eq_desc_set_no_orp(&eqdesc, 0); ++ qbman_eq_desc_set_response(&eqdesc, eq_storage_phys, 0); ++ qbman_eq_desc_set_token(&eqdesc, 0x99); ++#ifdef QBMAN_USE_QD ++ /**********************************/ ++ /* Prepare a Queueing Destination */ ++ /**********************************/ ++ qbman_eq_desc_set_qd(&eqdesc, QBMAN_TEST_QDID, 0, 3); ++#else ++ qbman_eq_desc_set_fq(&eqdesc, QBMAN_TEST_FQID); ++#endif ++ ++ /******************/ ++ /* Try an enqueue */ ++ /******************/ ++ ret = qbman_swp_enqueue(swp, &eqdesc, ++ (const struct qbman_fd *)&fd); ++ BUG_ON(ret); ++ for (j = 0; j < 8; j++) ++ fd_eq[i].words[j] = *((uint32_t *)&fd + j); ++ fd_inc(&fd); ++ } ++} ++ ++static void do_push_dequeue(struct qbman_swp *swp) ++{ ++ int i, j; ++ const struct dpaa2_dq *dq_storage1; ++ const struct qbman_fd *__fd; ++ int loopvar; ++ ++ pr_info("*****QBMan_test: Start push dequeue\n"); ++ for (i = 0; i < NUM_DQ_FRAME; i++) { ++ DBG_POLL_START(loopvar); ++ do { ++ DBG_POLL_CHECK(loopvar); ++ dq_storage1 = qbman_swp_dqrr_next(swp); ++ } while (!dq_storage1); ++ if (dq_storage1) { ++ __fd = (const struct qbman_fd *) ++ dpaa2_dq_fd(dq_storage1); ++ for (j = 0; j < 8; j++) ++ fd_dq[i].words[j] = __fd->words[j]; ++ if (fd_cmp(&fd_eq[i], &fd_dq[i])) { ++ pr_info("enqueue FD is\n"); ++ hexdump(&fd_eq[i], 32); ++ pr_info("dequeue FD is\n"); ++ hexdump(&fd_dq[i], 32); ++ } ++ qbman_swp_dqrr_consume(swp, dq_storage1); ++ } else { ++ pr_info("The push dequeue fails\n"); ++ } ++ } ++} ++ ++static void do_pull_dequeue(struct qbman_swp *swp) ++{ ++ int i, j, ret; ++ const struct dpaa2_dq *dq_storage1; ++ const struct qbman_fd *__fd; ++ int loopvar; ++ ++ pr_info("*****QBMan_test: Dequeue %d frames with dq entry in DQRR\n", ++ NUM_DQ_IN_DQRR); ++ for (i = 0; i < NUM_DQ_IN_DQRR; i++) { ++ qbman_pull_desc_clear(&pulldesc); ++ qbman_pull_desc_set_storage(&pulldesc, NULL, 0, 0); ++ qbman_pull_desc_set_numframes(&pulldesc, 1); ++ qbman_pull_desc_set_fq(&pulldesc, QBMAN_TEST_FQID); ++ ++ ret = qbman_swp_pull(swp, &pulldesc); ++ BUG_ON(ret); ++ DBG_POLL_START(loopvar); ++ do { ++ DBG_POLL_CHECK(loopvar); ++ dq_storage1 = qbman_swp_dqrr_next(swp); ++ } while (!dq_storage1); ++ ++ if (dq_storage1) { ++ __fd = (const struct qbman_fd *) ++ dpaa2_dq_fd(dq_storage1); ++ for (j = 0; j < 8; j++) ++ fd_dq[i].words[j] = __fd->words[j]; ++ if (fd_cmp(&fd_eq[i], &fd_dq[i])) { ++ pr_info("enqueue FD is\n"); ++ hexdump(&fd_eq[i], 32); ++ pr_info("dequeue FD is\n"); ++ hexdump(&fd_dq[i], 32); ++ } ++ qbman_swp_dqrr_consume(swp, dq_storage1); ++ } else { ++ pr_info("Dequeue with dq entry in DQRR fails\n"); ++ } ++ } ++ ++ pr_info("*****QBMan_test: Dequeue %d frames with dq entry in memory\n", ++ NUM_DQ_IN_MEM); ++ for (i = 0; i < NUM_DQ_IN_MEM; i++) { ++ dq_storage_phys = virt_to_phys(&dq_storage[i]); ++ qbman_pull_desc_clear(&pulldesc); ++ qbman_pull_desc_set_storage(&pulldesc, &dq_storage[i], ++ dq_storage_phys, 1); ++ qbman_pull_desc_set_numframes(&pulldesc, 1); ++ qbman_pull_desc_set_fq(&pulldesc, QBMAN_TEST_FQID); ++ ret = qbman_swp_pull(swp, &pulldesc); ++ BUG_ON(ret); ++ ++ DBG_POLL_START(loopvar); ++ do { ++ DBG_POLL_CHECK(loopvar); ++ ret = qbman_result_has_new_result(swp, ++ &dq_storage[i]); ++ } while (!ret); ++ ++ if (ret) { ++ for (j = 0; j < 8; j++) ++ fd_dq[i + NUM_DQ_IN_DQRR].words[j] = ++ dq_storage[i].dont_manipulate_directly[j + 8]; ++ j = i + NUM_DQ_IN_DQRR; ++ if (fd_cmp(&fd_eq[j], &fd_dq[j])) { ++ pr_info("enqueue FD is\n"); ++ hexdump(&fd_eq[i + NUM_DQ_IN_DQRR], 32); ++ pr_info("dequeue FD is\n"); ++ hexdump(&fd_dq[i + NUM_DQ_IN_DQRR], 32); ++ hexdump(&dq_storage[i], 64); ++ } ++ } else { ++ pr_info("Dequeue with dq entry in memory fails\n"); ++ } ++ } ++} ++ ++static void release_buffer(struct qbman_swp *swp, unsigned int num) ++{ ++ int ret; ++ unsigned int i, j; ++ ++ qbman_release_desc_clear(&releasedesc); ++ qbman_release_desc_set_bpid(&releasedesc, QBMAN_TEST_BPID); ++ pr_info("*****QBMan_test: Release %d buffers to BP %d\n", ++ num, QBMAN_TEST_BPID); ++ for (i = 0; i < (num / 7 + 1); i++) { ++ j = ((num - i * 7) > 7) ? 7 : (num - i * 7); ++ ret = qbman_swp_release(swp, &releasedesc, &rbufs[i * 7], j); ++ BUG_ON(ret); ++ } ++} ++ ++static void acquire_buffer(struct qbman_swp *swp, unsigned int num) ++{ ++ int ret; ++ unsigned int i, j; ++ ++ pr_info("*****QBMan_test: Acquire %d buffers from BP %d\n", ++ num, QBMAN_TEST_BPID); ++ ++ for (i = 0; i < (num / 7 + 1); i++) { ++ j = ((num - i * 7) > 7) ? 7 : (num - i * 7); ++ ret = qbman_swp_acquire(swp, QBMAN_TEST_BPID, &abufs[i * 7], j); ++ BUG_ON(ret != j); ++ } ++} ++ ++static void buffer_pool_test(struct qbman_swp *swp) ++{ ++ struct qbman_attr info; ++ struct dpaa2_dq *bpscn_message; ++ dma_addr_t bpscn_phys; ++ uint64_t bpscn_ctx; ++ uint64_t ctx = 0xbbccddaadeadbeefull; ++ int i, ret; ++ uint32_t hw_targ; ++ ++ pr_info("*****QBMan_test: test buffer pool management\n"); ++ ret = qbman_bp_query(swp, QBMAN_TEST_BPID, &info); ++ qbman_bp_attr_get_bpscn_addr(&info, &bpscn_phys); ++ pr_info("The bpscn is %llx, info_phys is %llx\n", bpscn_phys, ++ virt_to_phys(&info)); ++ bpscn_message = phys_to_virt(bpscn_phys); ++ ++ for (i = 0; i < 320; i++) ++ rbufs[i] = 0xf00dabba01234567ull + i * 0x40; ++ ++ release_buffer(swp, 320); ++ ++ pr_info("QBMan_test: query the buffer pool\n"); ++ qbman_bp_query(swp, QBMAN_TEST_BPID, &info); ++ hexdump(&info, 64); ++ qbman_bp_attr_get_hw_targ(&info, &hw_targ); ++ pr_info("hw_targ is %d\n", hw_targ); ++ ++ /* Acquire buffers to trigger BPSCN */ ++ acquire_buffer(swp, 300); ++ /* BPSCN should be written to the memory */ ++ qbman_bp_query(swp, QBMAN_TEST_BPID, &info); ++ hexdump(&info, 64); ++ hexdump(bpscn_message, 64); ++ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); ++ /* There should be free buffers in the pool */ ++ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); ++ /* Buffer pool is depleted */ ++ BUG_ON(!qbman_result_bpscn_is_depleted(bpscn_message)); ++ /* The ctx should match */ ++ bpscn_ctx = qbman_result_bpscn_ctx(bpscn_message); ++ pr_info("BPSCN test: ctx %llx, bpscn_ctx %llx\n", ctx, bpscn_ctx); ++ BUG_ON(ctx != bpscn_ctx); ++ memset(bpscn_message, 0, sizeof(struct dpaa2_dq)); ++ ++ /* Re-seed the buffer pool to trigger BPSCN */ ++ release_buffer(swp, 240); ++ /* BPSCN should be written to the memory */ ++ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); ++ /* There should be free buffers in the pool */ ++ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); ++ /* Buffer pool is not depleted */ ++ BUG_ON(qbman_result_bpscn_is_depleted(bpscn_message)); ++ memset(bpscn_message, 0, sizeof(struct dpaa2_dq)); ++ ++ acquire_buffer(swp, 260); ++ /* BPSCN should be written to the memory */ ++ BUG_ON(!qbman_result_is_BPSCN(bpscn_message)); ++ /* There should be free buffers in the pool while BPSCN generated */ ++ BUG_ON(!(qbman_result_bpscn_has_free_bufs(bpscn_message))); ++ /* Buffer pool is depletion */ ++ BUG_ON(!qbman_result_bpscn_is_depleted(bpscn_message)); ++} ++ ++static void ceetm_test(struct qbman_swp *swp) ++{ ++ int i, j, ret; ++ ++ qbman_eq_desc_clear(&eqdesc); ++ qbman_eq_desc_set_no_orp(&eqdesc, 0); ++ qbman_eq_desc_set_fq(&eqdesc, QBMAN_TEST_LFQID); ++ pr_info("*****QBMan_test: Enqueue to LFQID %x\n", ++ QBMAN_TEST_LFQID); ++ for (i = 0; i < NUM_EQ_FRAME; i++) { ++ ret = qbman_swp_enqueue(swp, &eqdesc, ++ (const struct qbman_fd *)&fd); ++ BUG_ON(ret); ++ for (j = 0; j < 8; j++) ++ fd_eq[i].words[j] = *((uint32_t *)&fd + j); ++ fd_inc(&fd); ++ } ++} ++ ++int qbman_test(void) ++{ ++ struct qbman_swp_desc pd; ++ uint32_t reg; ++ ++ pd.cena_bar = ioremap_cache_ns(QBMAN_SWP_CENA_BASE + ++ QBMAN_PORTAL_IDX * 0x10000, 0x10000); ++ pd.cinh_bar = ioremap(QBMAN_SWP_CINH_BASE + ++ QBMAN_PORTAL_IDX * 0x10000, 0x10000); ++ ++ /* Detect whether the mc image is the test image with GPP setup */ ++ reg = readl_relaxed(pd.cena_bar + 0x4); ++ if (reg != 0xdeadbeef) { ++ pr_err("The MC image doesn't have GPP test setup, stop!\n"); ++ iounmap(pd.cena_bar); ++ iounmap(pd.cinh_bar); ++ return -1; ++ } ++ ++ pr_info("*****QBMan_test: Init QBMan SWP %d\n", QBMAN_PORTAL_IDX); ++ swp = qbman_swp_init(&pd); ++ if (!swp) { ++ iounmap(pd.cena_bar); ++ iounmap(pd.cinh_bar); ++ return -1; ++ } ++ ++ /*******************/ ++ /* Enqueue frames */ ++ /*******************/ ++ do_enqueue(swp); ++ ++ /*******************/ ++ /* Do pull dequeue */ ++ /*******************/ ++ do_pull_dequeue(swp); ++ ++ /*******************/ ++ /* Enqueue frames */ ++ /*******************/ ++ qbman_swp_push_set(swp, 0, 1); ++ qbman_swp_fq_schedule(swp, QBMAN_TEST_FQID); ++ do_enqueue(swp); ++ ++ /*******************/ ++ /* Do push dequeue */ ++ /*******************/ ++ do_push_dequeue(swp); ++ ++ /**************************/ ++ /* Test buffer pool funcs */ ++ /**************************/ ++ buffer_pool_test(swp); ++ ++ /******************/ ++ /* CEETM test */ ++ /******************/ ++ ceetm_test(swp); ++ ++ qbman_swp_finish(swp); ++ pr_info("*****QBMan_test: Kernel test Passed\n"); ++ return 0; ++} ++ ++/* user-space test-case, definitions: ++ * ++ * 1 portal only, using portal index 3. ++ */ ++ ++#include <linux/uaccess.h> ++#include <linux/ioctl.h> ++#include <linux/miscdevice.h> ++#include <linux/fs.h> ++#include <linux/cdev.h> ++#include <linux/mm.h> ++#include <linux/mman.h> ++ ++#define QBMAN_TEST_US_SWP 3 /* portal index for user space */ ++ ++#define QBMAN_TEST_MAGIC 'q' ++struct qbman_test_swp_ioctl { ++ unsigned long portal1_cinh; ++ unsigned long portal1_cena; ++}; ++struct qbman_test_dma_ioctl { ++ unsigned long ptr; ++ uint64_t phys_addr; ++}; ++ ++struct qbman_test_priv { ++ int has_swp_map; ++ int has_dma_map; ++ unsigned long pgoff; ++}; ++ ++#define QBMAN_TEST_SWP_MAP \ ++ _IOR(QBMAN_TEST_MAGIC, 0x01, struct qbman_test_swp_ioctl) ++#define QBMAN_TEST_SWP_UNMAP \ ++ _IOR(QBMAN_TEST_MAGIC, 0x02, struct qbman_test_swp_ioctl) ++#define QBMAN_TEST_DMA_MAP \ ++ _IOR(QBMAN_TEST_MAGIC, 0x03, struct qbman_test_dma_ioctl) ++#define QBMAN_TEST_DMA_UNMAP \ ++ _IOR(QBMAN_TEST_MAGIC, 0x04, struct qbman_test_dma_ioctl) ++ ++#define TEST_PORTAL1_CENA_PGOFF ((QBMAN_SWP_CENA_BASE + QBMAN_TEST_US_SWP * \ ++ 0x10000) >> PAGE_SHIFT) ++#define TEST_PORTAL1_CINH_PGOFF ((QBMAN_SWP_CINH_BASE + QBMAN_TEST_US_SWP * \ ++ 0x10000) >> PAGE_SHIFT) ++ ++static int qbman_test_open(struct inode *inode, struct file *filp) ++{ ++ struct qbman_test_priv *priv; ++ ++ priv = kmalloc(sizeof(struct qbman_test_priv), GFP_KERNEL); ++ if (!priv) ++ return -EIO; ++ filp->private_data = priv; ++ priv->has_swp_map = 0; ++ priv->has_dma_map = 0; ++ priv->pgoff = 0; ++ return 0; ++} ++ ++static int qbman_test_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ int ret; ++ struct qbman_test_priv *priv = filp->private_data; ++ ++ BUG_ON(!priv); ++ ++ if (vma->vm_pgoff == TEST_PORTAL1_CINH_PGOFF) ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ else if (vma->vm_pgoff == TEST_PORTAL1_CENA_PGOFF) ++ vma->vm_page_prot = pgprot_cached_ns(vma->vm_page_prot); ++ else if (vma->vm_pgoff == priv->pgoff) ++ vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); ++ else { ++ pr_err("Damn, unrecognised pg_off!!\n"); ++ return -EINVAL; ++ } ++ ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot); ++ return ret; ++} ++ ++static long qbman_test_ioctl(struct file *fp, unsigned int cmd, ++ unsigned long arg) ++{ ++ void __user *a = (void __user *)arg; ++ unsigned long longret, populate; ++ int ret = 0; ++ struct qbman_test_priv *priv = fp->private_data; ++ ++ BUG_ON(!priv); ++ ++ switch (cmd) { ++ case QBMAN_TEST_SWP_MAP: ++ { ++ struct qbman_test_swp_ioctl params; ++ ++ if (priv->has_swp_map) ++ return -EINVAL; ++ down_write(¤t->mm->mmap_sem); ++ /* Map portal1 CINH */ ++ longret = do_mmap_pgoff(fp, PAGE_SIZE, 0x10000, ++ PROT_READ | PROT_WRITE, MAP_SHARED, ++ TEST_PORTAL1_CINH_PGOFF, &populate); ++ if (longret & ~PAGE_MASK) { ++ ret = (int)longret; ++ goto out; ++ } ++ params.portal1_cinh = longret; ++ /* Map portal1 CENA */ ++ longret = do_mmap_pgoff(fp, PAGE_SIZE, 0x10000, ++ PROT_READ | PROT_WRITE, MAP_SHARED, ++ TEST_PORTAL1_CENA_PGOFF, &populate); ++ if (longret & ~PAGE_MASK) { ++ ret = (int)longret; ++ goto out; ++ } ++ params.portal1_cena = longret; ++ priv->has_swp_map = 1; ++out: ++ up_write(¤t->mm->mmap_sem); ++ if (!ret && copy_to_user(a, ¶ms, sizeof(params))) ++ return -EFAULT; ++ return ret; ++ } ++ case QBMAN_TEST_SWP_UNMAP: ++ { ++ struct qbman_test_swp_ioctl params; ++ ++ if (!priv->has_swp_map) ++ return -EINVAL; ++ ++ if (copy_from_user(¶ms, a, sizeof(params))) ++ return -EFAULT; ++ down_write(¤t->mm->mmap_sem); ++ do_munmap(current->mm, params.portal1_cena, 0x10000); ++ do_munmap(current->mm, params.portal1_cinh, 0x10000); ++ up_write(¤t->mm->mmap_sem); ++ priv->has_swp_map = 0; ++ return 0; ++ } ++ case QBMAN_TEST_DMA_MAP: ++ { ++ struct qbman_test_dma_ioctl params; ++ void *vaddr; ++ ++ if (priv->has_dma_map) ++ return -EINVAL; ++ vaddr = (void *)get_zeroed_page(GFP_KERNEL); ++ params.phys_addr = virt_to_phys(vaddr); ++ priv->pgoff = (unsigned long)params.phys_addr >> PAGE_SHIFT; ++ down_write(¤t->mm->mmap_sem); ++ longret = do_mmap_pgoff(fp, PAGE_SIZE, PAGE_SIZE, ++ PROT_READ | PROT_WRITE, MAP_SHARED, ++ priv->pgoff, &populate); ++ if (longret & ~PAGE_MASK) { ++ ret = (int)longret; ++ return ret; ++ } ++ params.ptr = longret; ++ priv->has_dma_map = 1; ++ up_write(¤t->mm->mmap_sem); ++ if (copy_to_user(a, ¶ms, sizeof(params))) ++ return -EFAULT; ++ return 0; ++ } ++ case QBMAN_TEST_DMA_UNMAP: ++ { ++ struct qbman_test_dma_ioctl params; ++ ++ if (!priv->has_dma_map) ++ return -EINVAL; ++ if (copy_from_user(¶ms, a, sizeof(params))) ++ return -EFAULT; ++ down_write(¤t->mm->mmap_sem); ++ do_munmap(current->mm, params.ptr, PAGE_SIZE); ++ up_write(¤t->mm->mmap_sem); ++ free_page((unsigned long)phys_to_virt(params.phys_addr)); ++ priv->has_dma_map = 0; ++ return 0; ++ } ++ default: ++ pr_err("Bad ioctl cmd!\n"); ++ } ++ return -EINVAL; ++} ++ ++static const struct file_operations qbman_fops = { ++ .open = qbman_test_open, ++ .mmap = qbman_test_mmap, ++ .unlocked_ioctl = qbman_test_ioctl ++}; ++ ++static struct miscdevice qbman_miscdev = { ++ .name = "qbman-test", ++ .fops = &qbman_fops, ++ .minor = MISC_DYNAMIC_MINOR, ++}; ++ ++static int qbman_miscdev_init; ++ ++static int test_init(void) ++{ ++ int ret = qbman_test(); ++ ++ if (!ret) { ++ /* MC image supports the test cases, so instantiate the ++ * character devic that the user-space test case will use to do ++ * its memory mappings. */ ++ ret = misc_register(&qbman_miscdev); ++ if (ret) { ++ pr_err("qbman-test: failed to register misc device\n"); ++ return ret; ++ } ++ pr_info("qbman-test: misc device registered!\n"); ++ qbman_miscdev_init = 1; ++ } ++ return 0; ++} ++ ++static void test_exit(void) ++{ ++ if (qbman_miscdev_init) { ++ misc_deregister(&qbman_miscdev); ++ qbman_miscdev_init = 0; ++ } ++} ++ ++module_init(test_init); ++module_exit(test_exit); +--- /dev/null ++++ b/drivers/staging/fsl-mc/include/fsl_dpaa2_fd.h +@@ -0,0 +1,774 @@ ++/* 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 __FSL_DPAA2_FD_H ++#define __FSL_DPAA2_FD_H ++ ++/** ++ * DOC: DPAA2 FD - Frame Descriptor APIs for DPAA2 ++ * ++ * Frame Descriptors (FDs) are used to describe frame data in the DPAA2. ++ * Frames can be enqueued and dequeued to Frame Queues which are consumed ++ * by the various DPAA accelerators (WRIOP, SEC, PME, DCE) ++ * ++ * There are three types of frames: Single, Scatter Gather and Frame Lists. ++ * ++ * The set of APIs in this file must be used to create, manipulate and ++ * query Frame Descriptor. ++ * ++ */ ++ ++/** ++ * struct dpaa2_fd - Place-holder for FDs. ++ * @words: for easier/faster copying the whole FD structure. ++ * @addr_lo: the lower 32 bits of the address in FD. ++ * @addr_hi: the upper 32 bits of the address in FD. ++ * @len: the length field in FD. ++ * @bpid_offset: represent the bpid and offset fields in FD ++ * @frc: frame context ++ * @ctrl: the 32bit control bits including dd, sc,... va, err. ++ * @flc_lo: the lower 32bit of flow context. ++ * @flc_hi: the upper 32bits of flow context. ++ * ++ * This structure represents the basic Frame Descriptor used in the system. ++ * We represent it via the simplest form that we need for now. Different ++ * overlays may be needed to support different options, etc. (It is impractical ++ * to define One True Struct, because the resulting encoding routines (lots of ++ * read-modify-writes) would be worst-case performance whether or not ++ * circumstances required them.) ++ */ ++struct dpaa2_fd { ++ union { ++ u32 words[8]; ++ struct dpaa2_fd_simple { ++ u32 addr_lo; ++ u32 addr_hi; ++ u32 len; ++ /* offset in the MS 16 bits, BPID in the LS 16 bits */ ++ u32 bpid_offset; ++ u32 frc; /* frame context */ ++ /* "err", "va", "cbmt", "asal", [...] */ ++ u32 ctrl; ++ /* flow context */ ++ u32 flc_lo; ++ u32 flc_hi; ++ } simple; ++ }; ++}; ++ ++enum dpaa2_fd_format { ++ dpaa2_fd_single = 0, ++ dpaa2_fd_list, ++ dpaa2_fd_sg ++}; ++ ++/* Accessors for SG entry fields ++ * ++ * These setters and getters assume little endian format. For converting ++ * between LE and cpu endianness, the specific conversion functions must be ++ * called before the SGE contents are accessed by the core (on Rx), ++ * respectively before the SG table is sent to hardware (on Tx) ++ */ ++ ++/** ++ * dpaa2_fd_get_addr() - get the addr field of frame descriptor ++ * @fd: the given frame descriptor. ++ * ++ * Return the address in the frame descriptor. ++ */ ++static inline dma_addr_t dpaa2_fd_get_addr(const struct dpaa2_fd *fd) ++{ ++ return (dma_addr_t)((((uint64_t)fd->simple.addr_hi) << 32) ++ + fd->simple.addr_lo); ++} ++ ++/** ++ * dpaa2_fd_set_addr() - Set the addr field of frame descriptor ++ * @fd: the given frame descriptor. ++ * @addr: the address needs to be set in frame descriptor. ++ */ ++static inline void dpaa2_fd_set_addr(struct dpaa2_fd *fd, dma_addr_t addr) ++{ ++ fd->simple.addr_hi = upper_32_bits(addr); ++ fd->simple.addr_lo = lower_32_bits(addr); ++} ++ ++/** ++ * dpaa2_fd_get_frc() - Get the frame context in the frame descriptor ++ * @fd: the given frame descriptor. ++ * ++ * Return the frame context field in the frame descriptor. ++ */ ++static inline u32 dpaa2_fd_get_frc(const struct dpaa2_fd *fd) ++{ ++ return fd->simple.frc; ++} ++ ++/** ++ * dpaa2_fd_set_frc() - Set the frame context in the frame descriptor ++ * @fd: the given frame descriptor. ++ * @frc: the frame context needs to be set in frame descriptor. ++ */ ++static inline void dpaa2_fd_set_frc(struct dpaa2_fd *fd, u32 frc) ++{ ++ fd->simple.frc = frc; ++} ++ ++/** ++ * dpaa2_fd_get_flc() - Get the flow context in the frame descriptor ++ * @fd: the given frame descriptor. ++ * ++ * Return the flow context in the frame descriptor. ++ */ ++static inline dma_addr_t dpaa2_fd_get_flc(const struct dpaa2_fd *fd) ++{ ++ return (dma_addr_t)((((uint64_t)fd->simple.flc_hi) << 32) + ++ fd->simple.flc_lo); ++} ++ ++/** ++ * dpaa2_fd_set_flc() - Set the flow context field of frame descriptor ++ * @fd: the given frame descriptor. ++ * @flc_addr: the flow context needs to be set in frame descriptor. ++ */ ++static inline void dpaa2_fd_set_flc(struct dpaa2_fd *fd, dma_addr_t flc_addr) ++{ ++ fd->simple.flc_hi = upper_32_bits(flc_addr); ++ fd->simple.flc_lo = lower_32_bits(flc_addr); ++} ++ ++/** ++ * dpaa2_fd_get_len() - Get the length in the frame descriptor ++ * @fd: the given frame descriptor. ++ * ++ * Return the length field in the frame descriptor. ++ */ ++static inline u32 dpaa2_fd_get_len(const struct dpaa2_fd *fd) ++{ ++ return fd->simple.len; ++} ++ ++/** ++ * dpaa2_fd_set_len() - Set the length field of frame descriptor ++ * @fd: the given frame descriptor. ++ * @len: the length needs to be set in frame descriptor. ++ */ ++static inline void dpaa2_fd_set_len(struct dpaa2_fd *fd, u32 len) ++{ ++ fd->simple.len = len; ++} ++ ++/** ++ * dpaa2_fd_get_offset() - Get the offset field in the frame descriptor ++ * @fd: the given frame descriptor. ++ * ++ * Return the offset. ++ */ ++static inline uint16_t dpaa2_fd_get_offset(const struct dpaa2_fd *fd) ++{ ++ return (uint16_t)(fd->simple.bpid_offset >> 16) & 0x0FFF; ++} ++ ++/** ++ * dpaa2_fd_set_offset() - Set the offset field of frame descriptor ++ * ++ * @fd: the given frame descriptor. ++ * @offset: the offset needs to be set in frame descriptor. ++ */ ++static inline void dpaa2_fd_set_offset(struct dpaa2_fd *fd, uint16_t offset) ++{ ++ fd->simple.bpid_offset &= 0xF000FFFF; ++ fd->simple.bpid_offset |= (u32)offset << 16; ++} ++ ++/** ++ * dpaa2_fd_get_format() - Get the format field in the frame descriptor ++ * @fd: the given frame descriptor. ++ * ++ * Return the format. ++ */ ++static inline enum dpaa2_fd_format dpaa2_fd_get_format( ++ const struct dpaa2_fd *fd) ++{ ++ return (enum dpaa2_fd_format)((fd->simple.bpid_offset >> 28) & 0x3); ++} ++ ++/** ++ * dpaa2_fd_set_format() - Set the format field of frame descriptor ++ * ++ * @fd: the given frame descriptor. ++ * @format: the format needs to be set in frame descriptor. ++ */ ++static inline void dpaa2_fd_set_format(struct dpaa2_fd *fd, ++ enum dpaa2_fd_format format) ++{ ++ fd->simple.bpid_offset &= 0xCFFFFFFF; ++ fd->simple.bpid_offset |= (u32)format << 28; ++} ++ ++/** ++ * dpaa2_fd_get_bpid() - Get the bpid field in the frame descriptor ++ * @fd: the given frame descriptor. ++ * ++ * Return the bpid. ++ */ ++static inline uint16_t dpaa2_fd_get_bpid(const struct dpaa2_fd *fd) ++{ ++ return (uint16_t)(fd->simple.bpid_offset & 0xFFFF); ++} ++ ++/** ++ * dpaa2_fd_set_bpid() - Set the bpid field of frame descriptor ++ * ++ * @fd: the given frame descriptor. ++ * @bpid: the bpid needs to be set in frame descriptor. ++ */ ++static inline void dpaa2_fd_set_bpid(struct dpaa2_fd *fd, uint16_t bpid) ++{ ++ fd->simple.bpid_offset &= 0xFFFF0000; ++ fd->simple.bpid_offset |= (u32)bpid; ++} ++ ++/** ++ * struct dpaa2_sg_entry - the scatter-gathering structure ++ * @addr_lo: the lower 32bit of address ++ * @addr_hi: the upper 32bit of address ++ * @len: the length in this sg entry. ++ * @bpid_offset: offset in the MS 16 bits, BPID in the LS 16 bits. ++ */ ++struct dpaa2_sg_entry { ++ u32 addr_lo; ++ u32 addr_hi; ++ u32 len; ++ u32 bpid_offset; ++}; ++ ++enum dpaa2_sg_format { ++ dpaa2_sg_single = 0, ++ dpaa2_sg_frame_data, ++ dpaa2_sg_sgt_ext ++}; ++ ++/** ++ * dpaa2_sg_get_addr() - Get the address from SG entry ++ * @sg: the given scatter-gathering object. ++ * ++ * Return the address. ++ */ ++static inline dma_addr_t dpaa2_sg_get_addr(const struct dpaa2_sg_entry *sg) ++{ ++ return (dma_addr_t)((((u64)sg->addr_hi) << 32) + sg->addr_lo); ++} ++ ++/** ++ * dpaa2_sg_set_addr() - Set the address in SG entry ++ * @sg: the given scatter-gathering object. ++ * @addr: the address to be set. ++ */ ++static inline void dpaa2_sg_set_addr(struct dpaa2_sg_entry *sg, dma_addr_t addr) ++{ ++ sg->addr_hi = upper_32_bits(addr); ++ sg->addr_lo = lower_32_bits(addr); ++} ++ ++ ++static inline bool dpaa2_sg_short_len(const struct dpaa2_sg_entry *sg) ++{ ++ return (sg->bpid_offset >> 30) & 0x1; ++} ++ ++/** ++ * dpaa2_sg_get_len() - Get the length in SG entry ++ * @sg: the given scatter-gathering object. ++ * ++ * Return the length. ++ */ ++static inline u32 dpaa2_sg_get_len(const struct dpaa2_sg_entry *sg) ++{ ++ if (dpaa2_sg_short_len(sg)) ++ return sg->len & 0x1FFFF; ++ return sg->len; ++} ++ ++/** ++ * dpaa2_sg_set_len() - Set the length in SG entry ++ * @sg: the given scatter-gathering object. ++ * @len: the length to be set. ++ */ ++static inline void dpaa2_sg_set_len(struct dpaa2_sg_entry *sg, u32 len) ++{ ++ sg->len = len; ++} ++ ++/** ++ * dpaa2_sg_get_offset() - Get the offset in SG entry ++ * @sg: the given scatter-gathering object. ++ * ++ * Return the offset. ++ */ ++static inline u16 dpaa2_sg_get_offset(const struct dpaa2_sg_entry *sg) ++{ ++ return (u16)(sg->bpid_offset >> 16) & 0x0FFF; ++} ++ ++/** ++ * dpaa2_sg_set_offset() - Set the offset in SG entry ++ * @sg: the given scatter-gathering object. ++ * @offset: the offset to be set. ++ */ ++static inline void dpaa2_sg_set_offset(struct dpaa2_sg_entry *sg, ++ u16 offset) ++{ ++ sg->bpid_offset &= 0xF000FFFF; ++ sg->bpid_offset |= (u32)offset << 16; ++} ++ ++/** ++ * dpaa2_sg_get_format() - Get the SG format in SG entry ++ * @sg: the given scatter-gathering object. ++ * ++ * Return the format. ++ */ ++static inline enum dpaa2_sg_format ++ dpaa2_sg_get_format(const struct dpaa2_sg_entry *sg) ++{ ++ return (enum dpaa2_sg_format)((sg->bpid_offset >> 28) & 0x3); ++} ++ ++/** ++ * dpaa2_sg_set_format() - Set the SG format in SG entry ++ * @sg: the given scatter-gathering object. ++ * @format: the format to be set. ++ */ ++static inline void dpaa2_sg_set_format(struct dpaa2_sg_entry *sg, ++ enum dpaa2_sg_format format) ++{ ++ sg->bpid_offset &= 0xCFFFFFFF; ++ sg->bpid_offset |= (u32)format << 28; ++} ++ ++/** ++ * dpaa2_sg_get_bpid() - Get the buffer pool id in SG entry ++ * @sg: the given scatter-gathering object. ++ * ++ * Return the bpid. ++ */ ++static inline u16 dpaa2_sg_get_bpid(const struct dpaa2_sg_entry *sg) ++{ ++ return (u16)(sg->bpid_offset & 0x3FFF); ++} ++ ++/** ++ * dpaa2_sg_set_bpid() - Set the buffer pool id in SG entry ++ * @sg: the given scatter-gathering object. ++ * @bpid: the bpid to be set. ++ */ ++static inline void dpaa2_sg_set_bpid(struct dpaa2_sg_entry *sg, u16 bpid) ++{ ++ sg->bpid_offset &= 0xFFFFC000; ++ sg->bpid_offset |= (u32)bpid; ++} ++ ++/** ++ * dpaa2_sg_is_final() - Check final bit in SG entry ++ * @sg: the given scatter-gathering object. ++ * ++ * Return bool. ++ */ ++static inline bool dpaa2_sg_is_final(const struct dpaa2_sg_entry *sg) ++{ ++ return !!(sg->bpid_offset >> 31); ++} ++ ++/** ++ * dpaa2_sg_set_final() - Set the final bit in SG entry ++ * @sg: the given scatter-gathering object. ++ * @final: the final boolean to be set. ++ */ ++static inline void dpaa2_sg_set_final(struct dpaa2_sg_entry *sg, bool final) ++{ ++ sg->bpid_offset &= 0x7FFFFFFF; ++ sg->bpid_offset |= (u32)final << 31; ++} ++ ++/* Endianness conversion helper functions ++ * The accelerator drivers which construct / read scatter gather entries ++ * need to call these in order to account for endianness mismatches between ++ * hardware and cpu ++ */ ++#ifdef __BIG_ENDIAN ++/** ++ * dpaa2_sg_cpu_to_le() - convert scatter gather entry from native cpu ++ * format little endian format. ++ * @sg: the given scatter gather entry. ++ */ ++static inline void dpaa2_sg_cpu_to_le(struct dpaa2_sg_entry *sg) ++{ ++ uint32_t *p = (uint32_t *)sg; ++ int i; ++ ++ for (i = 0; i < sizeof(*sg) / sizeof(u32); i++) ++ cpu_to_le32s(p++); ++} ++ ++/** ++ * dpaa2_sg_le_to_cpu() - convert scatter gather entry from little endian ++ * format to native cpu format. ++ * @sg: the given scatter gather entry. ++ */ ++static inline void dpaa2_sg_le_to_cpu(struct dpaa2_sg_entry *sg) ++{ ++ uint32_t *p = (uint32_t *)sg; ++ int i; ++ ++ for (i = 0; i < sizeof(*sg) / sizeof(u32); i++) ++ le32_to_cpus(p++); ++} ++#else ++#define dpaa2_sg_cpu_to_le(sg) ++#define dpaa2_sg_le_to_cpu(sg) ++#endif /* __BIG_ENDIAN */ ++ ++ ++/** ++ * struct dpaa2_fl_entry - structure for frame list entry. ++ * @addr_lo: the lower 32bit of address ++ * @addr_hi: the upper 32bit of address ++ * @len: the length in this sg entry. ++ * @bpid_offset: offset in the MS 16 bits, BPID in the LS 16 bits. ++ * @frc: frame context ++ * @ctrl: the 32bit control bits including dd, sc,... va, err. ++ * @flc_lo: the lower 32bit of flow context. ++ * @flc_hi: the upper 32bits of flow context. ++ * ++ * Frame List Entry (FLE) ++ * Identical to dpaa2_fd.simple layout, but some bits are different ++ */ ++struct dpaa2_fl_entry { ++ u32 addr_lo; ++ u32 addr_hi; ++ u32 len; ++ u32 bpid_offset; ++ u32 frc; ++ u32 ctrl; ++ u32 flc_lo; ++ u32 flc_hi; ++}; ++ ++enum dpaa2_fl_format { ++ dpaa2_fl_single = 0, ++ dpaa2_fl_res, ++ dpaa2_fl_sg ++}; ++ ++/** ++ * dpaa2_fl_get_addr() - Get address in the frame list entry ++ * @fle: the given frame list entry. ++ * ++ * Return address for the get function. ++ */ ++static inline dma_addr_t dpaa2_fl_get_addr(const struct dpaa2_fl_entry *fle) ++{ ++ return (dma_addr_t)((((uint64_t)fle->addr_hi) << 32) + fle->addr_lo); ++} ++ ++/** ++ * dpaa2_fl_set_addr() - Set the address in the frame list entry ++ * @fle: the given frame list entry. ++ * @addr: the address needs to be set. ++ * ++ */ ++static inline void dpaa2_fl_set_addr(struct dpaa2_fl_entry *fle, ++ dma_addr_t addr) ++{ ++ fle->addr_hi = upper_32_bits(addr); ++ fle->addr_lo = lower_32_bits(addr); ++} ++ ++/** ++ * dpaa2_fl_get_flc() - Get the flow context in the frame list entry ++ * @fle: the given frame list entry. ++ * ++ * Return flow context for the get function. ++ */ ++static inline dma_addr_t dpaa2_fl_get_flc(const struct dpaa2_fl_entry *fle) ++{ ++ return (dma_addr_t)((((uint64_t)fle->flc_hi) << 32) + fle->flc_lo); ++} ++ ++/** ++ * dpaa2_fl_set_flc() - Set the flow context in the frame list entry ++ * @fle: the given frame list entry. ++ * @flc_addr: the flow context address needs to be set. ++ * ++ */ ++static inline void dpaa2_fl_set_flc(struct dpaa2_fl_entry *fle, ++ dma_addr_t flc_addr) ++{ ++ fle->flc_hi = upper_32_bits(flc_addr); ++ fle->flc_lo = lower_32_bits(flc_addr); ++} ++ ++/** ++ * dpaa2_fl_get_len() - Get the length in the frame list entry ++ * @fle: the given frame list entry. ++ * ++ * Return length for the get function. ++ */ ++static inline u32 dpaa2_fl_get_len(const struct dpaa2_fl_entry *fle) ++{ ++ return fle->len; ++} ++ ++/** ++ * dpaa2_fl_set_len() - Set the length in the frame list entry ++ * @fle: the given frame list entry. ++ * @len: the length needs to be set. ++ * ++ */ ++static inline void dpaa2_fl_set_len(struct dpaa2_fl_entry *fle, u32 len) ++{ ++ fle->len = len; ++} ++ ++/** ++ * dpaa2_fl_get_offset() - Get/Set the offset in the frame list entry ++ * @fle: the given frame list entry. ++ * ++ * Return offset for the get function. ++ */ ++static inline uint16_t dpaa2_fl_get_offset(const struct dpaa2_fl_entry *fle) ++{ ++ return (uint16_t)(fle->bpid_offset >> 16) & 0x0FFF; ++} ++ ++/** ++ * dpaa2_fl_set_offset() - Set the offset in the frame list entry ++ * @fle: the given frame list entry. ++ * @offset: the offset needs to be set. ++ * ++ */ ++static inline void dpaa2_fl_set_offset(struct dpaa2_fl_entry *fle, ++ uint16_t offset) ++{ ++ fle->bpid_offset &= 0xF000FFFF; ++ fle->bpid_offset |= (u32)(offset & 0x0FFF) << 16; ++} ++ ++/** ++ * dpaa2_fl_get_format() - Get the format in the frame list entry ++ * @fle: the given frame list entry. ++ * ++ * Return frame list format for the get function. ++ */ ++static inline enum dpaa2_fl_format dpaa2_fl_get_format( ++ const struct dpaa2_fl_entry *fle) ++{ ++ return (enum dpaa2_fl_format)((fle->bpid_offset >> 28) & 0x3); ++} ++ ++/** ++ * dpaa2_fl_set_format() - Set the format in the frame list entry ++ * @fle: the given frame list entry. ++ * @format: the frame list format needs to be set. ++ * ++ */ ++static inline void dpaa2_fl_set_format(struct dpaa2_fl_entry *fle, ++ enum dpaa2_fl_format format) ++{ ++ fle->bpid_offset &= 0xCFFFFFFF; ++ fle->bpid_offset |= (u32)(format & 0x3) << 28; ++} ++ ++/** ++ * dpaa2_fl_get_bpid() - Get the buffer pool id in the frame list entry ++ * @fle: the given frame list entry. ++ * ++ * Return bpid for the get function. ++ */ ++static inline uint16_t dpaa2_fl_get_bpid(const struct dpaa2_fl_entry *fle) ++{ ++ return (uint16_t)(fle->bpid_offset & 0x3FFF); ++} ++ ++/** ++ * dpaa2_fl_set_bpid() - Set the buffer pool id in the frame list entry ++ * @fle: the given frame list entry. ++ * @bpid: the buffer pool id needs to be set. ++ * ++ */ ++static inline void dpaa2_fl_set_bpid(struct dpaa2_fl_entry *fle, uint16_t bpid) ++{ ++ fle->bpid_offset &= 0xFFFFC000; ++ fle->bpid_offset |= (u32)bpid; ++} ++ ++/** dpaa2_fl_is_final() - check the final bit is set or not in the frame list. ++ * @fle: the given frame list entry. ++ * ++ * Return final bit settting. ++ */ ++static inline bool dpaa2_fl_is_final(const struct dpaa2_fl_entry *fle) ++{ ++ return !!(fle->bpid_offset >> 31); ++} ++ ++/** ++ * dpaa2_fl_set_final() - Set the final bit in the frame list entry ++ * @fle: the given frame list entry. ++ * @final: the final bit needs to be set. ++ * ++ */ ++static inline void dpaa2_fl_set_final(struct dpaa2_fl_entry *fle, bool final) ++{ ++ fle->bpid_offset &= 0x7FFFFFFF; ++ fle->bpid_offset |= (u32)final << 31; ++} ++ ++/** ++ * struct dpaa2_dq - the qman result structure ++ * @dont_manipulate_directly: the 16 32bit data to represent the whole ++ * possible qman dequeue result. ++ * ++ * When frames are dequeued, the FDs show up inside "dequeue" result structures ++ * (if at all, not all dequeue results contain valid FDs). This structure type ++ * is intentionally defined without internal detail, and the only reason it ++ * isn't declared opaquely (without size) is to allow the user to provide ++ * suitably-sized (and aligned) memory for these entries. ++ */ ++struct dpaa2_dq { ++ uint32_t dont_manipulate_directly[16]; ++}; ++ ++/* Parsing frame dequeue results */ ++/* FQ empty */ ++#define DPAA2_DQ_STAT_FQEMPTY 0x80 ++/* FQ held active */ ++#define DPAA2_DQ_STAT_HELDACTIVE 0x40 ++/* FQ force eligible */ ++#define DPAA2_DQ_STAT_FORCEELIGIBLE 0x20 ++/* Valid frame */ ++#define DPAA2_DQ_STAT_VALIDFRAME 0x10 ++/* FQ ODP enable */ ++#define DPAA2_DQ_STAT_ODPVALID 0x04 ++/* Volatile dequeue */ ++#define DPAA2_DQ_STAT_VOLATILE 0x02 ++/* volatile dequeue command is expired */ ++#define DPAA2_DQ_STAT_EXPIRED 0x01 ++ ++/** ++ * dpaa2_dq_flags() - Get the stat field of dequeue response ++ * @dq: the dequeue result. ++ */ ++uint32_t dpaa2_dq_flags(const struct dpaa2_dq *dq); ++ ++/** ++ * dpaa2_dq_is_pull() - Check whether the dq response is from a pull ++ * command. ++ * @dq: the dequeue result. ++ * ++ * Return 1 for volatile(pull) dequeue, 0 for static dequeue. ++ */ ++static inline int dpaa2_dq_is_pull(const struct dpaa2_dq *dq) ++{ ++ return (int)(dpaa2_dq_flags(dq) & DPAA2_DQ_STAT_VOLATILE); ++} ++ ++/** ++ * dpaa2_dq_is_pull_complete() - Check whether the pull command is completed. ++ * @dq: the dequeue result. ++ * ++ * Return boolean. ++ */ ++static inline int dpaa2_dq_is_pull_complete( ++ const struct dpaa2_dq *dq) ++{ ++ return (int)(dpaa2_dq_flags(dq) & DPAA2_DQ_STAT_EXPIRED); ++} ++ ++/** ++ * dpaa2_dq_seqnum() - Get the seqnum field in dequeue response ++ * seqnum is valid only if VALIDFRAME flag is TRUE ++ * @dq: the dequeue result. ++ * ++ * Return seqnum. ++ */ ++uint16_t dpaa2_dq_seqnum(const struct dpaa2_dq *dq); ++ ++/** ++ * dpaa2_dq_odpid() - Get the seqnum field in dequeue response ++ * odpid is valid only if ODPVAILD flag is TRUE. ++ * @dq: the dequeue result. ++ * ++ * Return odpid. ++ */ ++uint16_t dpaa2_dq_odpid(const struct dpaa2_dq *dq); ++ ++/** ++ * dpaa2_dq_fqid() - Get the fqid in dequeue response ++ * @dq: the dequeue result. ++ * ++ * Return fqid. ++ */ ++uint32_t dpaa2_dq_fqid(const struct dpaa2_dq *dq); ++ ++/** ++ * dpaa2_dq_byte_count() - Get the byte count in dequeue response ++ * @dq: the dequeue result. ++ * ++ * Return the byte count remaining in the FQ. ++ */ ++uint32_t dpaa2_dq_byte_count(const struct dpaa2_dq *dq); ++ ++/** ++ * dpaa2_dq_frame_count() - Get the frame count in dequeue response ++ * @dq: the dequeue result. ++ * ++ * Return the frame count remaining in the FQ. ++ */ ++uint32_t dpaa2_dq_frame_count(const struct dpaa2_dq *dq); ++ ++/** ++ * dpaa2_dq_fd_ctx() - Get the frame queue context in dequeue response ++ * @dq: the dequeue result. ++ * ++ * Return the frame queue context. ++ */ ++uint64_t dpaa2_dq_fqd_ctx(const struct dpaa2_dq *dq); ++ ++/** ++ * dpaa2_dq_fd() - Get the frame descriptor in dequeue response ++ * @dq: the dequeue result. ++ * ++ * Return the frame descriptor. ++ */ ++const struct dpaa2_fd *dpaa2_dq_fd(const struct dpaa2_dq *dq); ++ ++#endif /* __FSL_DPAA2_FD_H */ +--- /dev/null ++++ b/drivers/staging/fsl-mc/include/fsl_dpaa2_io.h +@@ -0,0 +1,619 @@ ++/* 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 __FSL_DPAA2_IO_H ++#define __FSL_DPAA2_IO_H ++ ++#include "fsl_dpaa2_fd.h" ++ ++struct dpaa2_io; ++struct dpaa2_io_store; ++ ++/** ++ * DOC: DPIO Service Management ++ * ++ * The DPIO service provides APIs for users to interact with the datapath ++ * by enqueueing and dequeing frame descriptors. ++ * ++ * The following set of APIs can be used to enqueue and dequeue frames ++ * as well as producing notification callbacks when data is available ++ * for dequeue. ++ */ ++ ++/** ++ * struct dpaa2_io_desc - The DPIO descriptor. ++ * @receives_notifications: Use notificaton mode. ++ * @has_irq: use irq-based proessing. ++ * @will_poll: use poll processing. ++ * @has_8prio: set for channel with 8 priority WQs. ++ * @cpu: the cpu index that at least interrupt handlers will execute on. ++ * @stash_affinity: the stash affinity for this portal favour 'cpu' ++ * @regs_cena: the cache enabled regs. ++ * @regs_cinh: the cache inhibited regs. ++ * @dpio_id: The dpio index. ++ * @qman_version: the qman version ++ * ++ * Describe the attributes and features of the DPIO object. ++ */ ++struct dpaa2_io_desc { ++ /* non-zero iff the DPIO has a channel */ ++ int receives_notifications; ++ /* non-zero if the DPIO portal interrupt is handled. If so, the ++ * caller/OS handles the interrupt and calls dpaa2_io_service_irq(). */ ++ int has_irq; ++ /* non-zero if the caller/OS is prepared to called the ++ * dpaa2_io_service_poll() routine as part of its run-to-completion (or ++ * scheduling) loop. If so, the DPIO service may dynamically switch some ++ * of its processing between polling-based and irq-based. It is illegal ++ * combination to have (!has_irq && !will_poll). */ ++ int will_poll; ++ /* ignored unless 'receives_notifications'. Non-zero iff the channel has ++ * 8 priority WQs, otherwise the channel has 2. */ ++ int has_8prio; ++ /* the cpu index that at least interrupt handlers will execute on. And ++ * if 'stash_affinity' is non-zero, the cache targeted by stash ++ * transactions is affine to this cpu. */ ++ int cpu; ++ /* non-zero if stash transactions for this portal favour 'cpu' over ++ * other CPUs. (Eg. zero if there's no stashing, or stashing is to ++ * shared cache.) */ ++ int stash_affinity; ++ /* Caller-provided flags, determined by bus-scanning and/or creation of ++ * DPIO objects via MC commands. */ ++ void *regs_cena; ++ void *regs_cinh; ++ int dpio_id; ++ uint32_t qman_version; ++}; ++ ++/** ++ * dpaa2_io_create() - create a dpaa2_io object. ++ * @desc: the dpaa2_io descriptor ++ * ++ * Activates a "struct dpaa2_io" corresponding to the given config of an actual ++ * DPIO object. This handle can be used on it's own (like a one-portal "DPIO ++ * service") or later be added to a service-type "struct dpaa2_io" object. Note, ++ * the information required on 'cfg' is copied so the caller is free to do as ++ * they wish with the input parameter upon return. ++ * ++ * Return a valid dpaa2_io object for success, or NULL for failure. ++ */ ++struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc); ++ ++/** ++ * dpaa2_io_create_service() - Create an (initially empty) DPIO service. ++ * ++ * Return a valid dpaa2_io object for success, or NULL for failure. ++ */ ++struct dpaa2_io *dpaa2_io_create_service(void); ++ ++/** ++ * dpaa2_io_default_service() - Use the driver's own global (and initially ++ * empty) DPIO service. ++ * ++ * This increments the reference count, so don't forget to use dpaa2_io_down() ++ * for each time this function is called. ++ * ++ * Return a valid dpaa2_io object for success, or NULL for failure. ++ */ ++struct dpaa2_io *dpaa2_io_default_service(void); ++ ++/** ++ * dpaa2_io_down() - release the dpaa2_io object. ++ * @d: the dpaa2_io object to be released. ++ * ++ * The "struct dpaa2_io" type can represent an individual DPIO object (as ++ * described by "struct dpaa2_io_desc") or an instance of a "DPIO service", ++ * which can be used to group/encapsulate multiple DPIO objects. In all cases, ++ * each handle obtained should be released using this function. ++ */ ++void dpaa2_io_down(struct dpaa2_io *d); ++ ++/** ++ * dpaa2_io_service_add() - Add the given DPIO object to the given DPIO service. ++ * @service: the given DPIO service. ++ * @obj: the given DPIO object. ++ * ++ * 'service' must have been created by dpaa2_io_create_service() and 'obj' ++ * must have been created by dpaa2_io_create(). This increments the reference ++ * count on the object that 'obj' refers to, so the user could call ++ * dpaa2_io_down(obj) after this and the object will persist within the service ++ * (and will be destroyed when the service is destroyed). ++ * ++ * Return 0 for success, or -EINVAL for failure. ++ */ ++int dpaa2_io_service_add(struct dpaa2_io *service, struct dpaa2_io *obj); ++ ++/** ++ * dpaa2_io_get_descriptor() - Get the DPIO descriptor of the given DPIO object. ++ * @obj: the given DPIO object. ++ * @desc: the returned DPIO descriptor. ++ * ++ * This function will return failure if the given dpaa2_io struct represents a ++ * service rather than an individual DPIO object, otherwise it returns zero and ++ * the given 'cfg' structure is filled in. ++ * ++ * Return 0 for success, or -EINVAL for failure. ++ */ ++int dpaa2_io_get_descriptor(struct dpaa2_io *obj, struct dpaa2_io_desc *desc); ++ ++/** ++ * dpaa2_io_poll() - Process any notifications and h/w-initiated events that ++ * are polling-driven. ++ * @obj: the given DPIO object. ++ * ++ * Obligatory for DPIO objects that have dpaa2_io_desc::will_poll non-zero. ++ * ++ * Return 0 for success, or -EINVAL for failure. ++ */ ++int dpaa2_io_poll(struct dpaa2_io *obj); ++ ++/** ++ * dpaa2_io_irq() - Process any notifications and h/w-initiated events that are ++ * irq-driven. ++ * @obj: the given DPIO object. ++ * ++ * Obligatory for DPIO objects that have dpaa2_io_desc::has_irq non-zero. ++ * ++ * Return IRQ_HANDLED for success, or -EINVAL for failure. ++ */ ++int dpaa2_io_irq(struct dpaa2_io *obj); ++ ++/** ++ * dpaa2_io_pause_poll() - Used to stop polling. ++ * @obj: the given DPIO object. ++ * ++ * If a polling application is going to stop polling for a period of time and ++ * supports interrupt processing, it can call this function to convert all ++ * processing to IRQ. (Eg. when sleeping.) ++ * ++ * Return -EINVAL. ++ */ ++int dpaa2_io_pause_poll(struct dpaa2_io *obj); ++ ++/** ++ * dpaa2_io_resume_poll() - Resume polling ++ * @obj: the given DPIO object. ++ * ++ * Return -EINVAL. ++ */ ++int dpaa2_io_resume_poll(struct dpaa2_io *obj); ++ ++/** ++ * dpaa2_io_service_notifications() - Get a mask of cpus that the DPIO service ++ * can receive notifications on. ++ * @s: the given DPIO object. ++ * @mask: the mask of cpus. ++ * ++ * Note that this is a run-time snapshot. If things like cpu-hotplug are ++ * supported in the target system, then an attempt to register notifications ++ * for a cpu that appears present in the given mask might fail if that cpu has ++ * gone offline in the mean time. ++ */ ++void dpaa2_io_service_notifications(struct dpaa2_io *s, cpumask_t *mask); ++ ++/** ++ * dpaa2_io_service_stashing - Get a mask of cpus that the DPIO service has stash ++ * affinity to. ++ * @s: the given DPIO object. ++ * @mask: the mask of cpus. ++ */ ++void dpaa2_io_service_stashing(struct dpaa2_io *s, cpumask_t *mask); ++ ++/** ++ * dpaa2_io_service_nonaffine() - Check the DPIO service's cpu affinity ++ * for stashing. ++ * @s: the given DPIO object. ++ * ++ * Return a boolean, whether or not the DPIO service has resources that have no ++ * particular cpu affinity for stashing. (Useful to know if you wish to operate ++ * on CPUs that the service has no affinity to, you would choose to use ++ * resources that are neutral, rather than affine to a different CPU.) Unlike ++ * other service-specific APIs, this one doesn't return an error if it is passed ++ * a non-service object. So don't do it. ++ */ ++int dpaa2_io_service_has_nonaffine(struct dpaa2_io *s); ++ ++/*************************/ ++/* Notification handling */ ++/*************************/ ++ ++/** ++ * struct dpaa2_io_notification_ctx - The DPIO notification context structure. ++ * @cb: the callback to be invoked when the notification arrives. ++ * @is_cdan: Zero/FALSE for FQDAN, non-zero/TRUE for CDAN. ++ * @id: FQID or channel ID, needed for rearm. ++ * @desired_cpu: the cpu on which the notifications will show up. ++ * @actual_cpu: the cpu the notification actually shows up. ++ * @migration_cb: callback function used for migration. ++ * @dpio_id: the dpio index. ++ * @qman64: the 64-bit context value shows up in the FQDAN/CDAN. ++ * @node: the list node. ++ * @dpio_private: the dpio object internal to dpio_service. ++ * ++ * When a FQDAN/CDAN registration is made (eg. by DPNI/DPCON/DPAI code), a ++ * context of the following type is used. The caller can embed it within a ++ * larger structure in order to add state that is tracked along with the ++ * notification (this may be useful when callbacks are invoked that pass this ++ * notification context as a parameter). ++ */ ++struct dpaa2_io_notification_ctx { ++ void (*cb)(struct dpaa2_io_notification_ctx *); ++ int is_cdan; ++ uint32_t id; ++ /* This specifies which cpu the user wants notifications to show up on ++ * (ie. to execute 'cb'). If notification-handling on that cpu is not ++ * available at the time of notification registration, the registration ++ * will fail. */ ++ int desired_cpu; ++ /* If the target platform supports cpu-hotplug or other features ++ * (related to power-management, one would expect) that can migrate IRQ ++ * handling of a given DPIO object, then this value will potentially be ++ * different to 'desired_cpu' at run-time. */ ++ int actual_cpu; ++ /* And if migration does occur and this callback is non-NULL, it will ++ * be invoked prior to any futher notification callbacks executing on ++ * 'newcpu'. Note that 'oldcpu' is what 'actual_cpu' was prior to the ++ * migration, and 'newcpu' is what it is now. Both could conceivably be ++ * different to 'desired_cpu'. */ ++ void (*migration_cb)(struct dpaa2_io_notification_ctx *, ++ int oldcpu, int newcpu); ++ /* These are returned from dpaa2_io_service_register(). ++ * 'dpio_id' is the dpaa2_io_desc::dpio_id value of the DPIO object that ++ * has been selected by the service for receiving the notifications. The ++ * caller can use this value in the MC command that attaches the FQ (or ++ * channel) of their DPNI (or DPCON, respectively) to this DPIO for ++ * notification-generation. ++ * 'qman64' is the 64-bit context value that needs to be sent in the ++ * same MC command in order to be programmed into the FQ or channel - ++ * this is the 64-bit value that shows up in the FQDAN/CDAN messages to ++ * the DPIO object, and the DPIO service specifies this value back to ++ * the caller so that the notifications that show up will be ++ * comprensible/demux-able to the DPIO service. */ ++ int dpio_id; ++ uint64_t qman64; ++ /* These fields are internal to the DPIO service once the context is ++ * registered. TBD: may require more internal state fields. */ ++ struct list_head node; ++ void *dpio_private; ++}; ++ ++/** ++ * dpaa2_io_service_register() - Prepare for servicing of FQDAN or CDAN ++ * notifications on the given DPIO service. ++ * @service: the given DPIO service. ++ * @ctx: the notification context. ++ * ++ * The MC command to attach the caller's DPNI/DPCON/DPAI device to a ++ * DPIO object is performed after this function is called. In that way, (a) the ++ * DPIO service is "ready" to handle a notification arrival (which might happen ++ * before the "attach" command to MC has returned control of execution back to ++ * the caller), and (b) the DPIO service can provide back to the caller the ++ * 'dpio_id' and 'qman64' parameters that it should pass along in the MC command ++ * in order for the DPNI/DPCON/DPAI resources to be configured to produce the ++ * right notification fields to the DPIO service. ++ * ++ * Return 0 for success, or -ENODEV for failure. ++ */ ++int dpaa2_io_service_register(struct dpaa2_io *service, ++ struct dpaa2_io_notification_ctx *ctx); ++ ++/** ++ * dpaa2_io_service_deregister - The opposite of 'register'. ++ * @service: the given DPIO service. ++ * @ctx: the notification context. ++ * ++ * Note that 'register' should be called *before* ++ * making the MC call to attach the notification-producing device to the ++ * notification-handling DPIO service, the 'unregister' function should be ++ * called *after* making the MC call to detach the notification-producing ++ * device. ++ * ++ * Return 0 for success. ++ */ ++int dpaa2_io_service_deregister(struct dpaa2_io *service, ++ struct dpaa2_io_notification_ctx *ctx); ++ ++/** ++ * dpaa2_io_service_rearm() - Rearm the notification for the given DPIO service. ++ * @service: the given DPIO service. ++ * @ctx: the notification context. ++ * ++ * Once a FQDAN/CDAN has been produced, the corresponding FQ/channel is ++ * considered "disarmed". Ie. the user can issue pull dequeue operations on that ++ * traffic source for as long as it likes. Eventually it may wish to "rearm" ++ * that source to allow it to produce another FQDAN/CDAN, that's what this ++ * function achieves. ++ * ++ * Return 0 for success, or -ENODEV if no service available, -EBUSY/-EIO for not ++ * being able to implement the rearm the notifiaton due to setting CDAN or ++ * scheduling fq. ++ */ ++int dpaa2_io_service_rearm(struct dpaa2_io *service, ++ struct dpaa2_io_notification_ctx *ctx); ++ ++/** ++ * dpaa2_io_from_registration() - Get the DPIO object from the given notification ++ * context. ++ * @ctx: the given notifiation context. ++ * @ret: the returned DPIO object. ++ * ++ * Like 'dpaa2_io_service_get_persistent()' (see below), except that the ++ * returned handle is not selected based on a 'cpu' argument, but is the same ++ * DPIO object that the given notification context is registered against. The ++ * returned handle carries a reference count, so a corresponding dpaa2_io_down() ++ * would be required when the reference is no longer needed. ++ * ++ * Return 0 for success, or -EINVAL for failure. ++ */ ++int dpaa2_io_from_registration(struct dpaa2_io_notification_ctx *ctx, ++ struct dpaa2_io **ret); ++ ++/**********************************/ ++/* General usage of DPIO services */ ++/**********************************/ ++ ++/** ++ * dpaa2_io_service_get_persistent() - Get the DPIO resource from the given ++ * notification context and cpu. ++ * @service: the DPIO service. ++ * @cpu: the cpu that the DPIO resource has stashing affinity to. ++ * @ret: the returned DPIO resource. ++ * ++ * The various DPIO interfaces can accept a "struct dpaa2_io" handle that refers ++ * to an individual DPIO object or to a whole service. In the latter case, an ++ * internal choice is made for each operation. This function supports the former ++ * case, by selecting an individual DPIO object *from* the service in order for ++ * it to be used multiple times to provide "persistence". The returned handle ++ * also carries a reference count, so a corresponding dpaa2_io_down() would be ++ * required when the reference is no longer needed. Note, a parameter of -1 for ++ * 'cpu' will select a DPIO resource that has no particular stashing affinity to ++ * any cpu (eg. one that stashes to platform cache). ++ * ++ * Return 0 for success, or -ENODEV for failure. ++ */ ++int dpaa2_io_service_get_persistent(struct dpaa2_io *service, int cpu, ++ struct dpaa2_io **ret); ++ ++/*****************/ ++/* Pull dequeues */ ++/*****************/ ++ ++/** ++ * dpaa2_io_service_pull_fq() - pull dequeue functions from a fq. ++ * @d: the given DPIO service. ++ * @fqid: the given frame queue id. ++ * @s: the dpaa2_io_store object for the result. ++ * ++ * To support DCA/order-preservation, it will be necessary to support an ++ * alternative form, because they must ultimately dequeue to DQRR rather than a ++ * user-supplied dpaa2_io_store. Furthermore, those dequeue results will ++ * "complete" using a caller-provided callback (from DQRR processing) rather ++ * than the caller explicitly looking at their dpaa2_io_store for results. Eg. ++ * the alternative form will likely take a callback parameter rather than a ++ * store parameter. Ignoring it for now to keep the picture clearer. ++ * ++ * Return 0 for success, or error code for failure. ++ */ ++int dpaa2_io_service_pull_fq(struct dpaa2_io *d, uint32_t fqid, ++ struct dpaa2_io_store *s); ++ ++/** ++ * dpaa2_io_service_pull_channel() - pull dequeue functions from a channel. ++ * @d: the given DPIO service. ++ * @channelid: the given channel id. ++ * @s: the dpaa2_io_store object for the result. ++ * ++ * To support DCA/order-preservation, it will be necessary to support an ++ * alternative form, because they must ultimately dequeue to DQRR rather than a ++ * user-supplied dpaa2_io_store. Furthermore, those dequeue results will ++ * "complete" using a caller-provided callback (from DQRR processing) rather ++ * than the caller explicitly looking at their dpaa2_io_store for results. Eg. ++ * the alternative form will likely take a callback parameter rather than a ++ * store parameter. Ignoring it for now to keep the picture clearer. ++ * ++ * Return 0 for success, or error code for failure. ++ */ ++int dpaa2_io_service_pull_channel(struct dpaa2_io *d, uint32_t channelid, ++ struct dpaa2_io_store *s); ++ ++/************/ ++/* Enqueues */ ++/************/ ++ ++/** ++ * dpaa2_io_service_enqueue_fq() - Enqueue a frame to a frame queue. ++ * @d: the given DPIO service. ++ * @fqid: the given frame queue id. ++ * @fd: the frame descriptor which is enqueued. ++ * ++ * This definition bypasses some features that are not expected to be priority-1 ++ * features, and may not be needed at all via current assumptions (QBMan's ++ * feature set is wider than the MC object model is intendeding to support, ++ * initially at least). Plus, keeping them out (for now) keeps the API view ++ * simpler. Missing features are; ++ * - enqueue confirmation (results DMA'd back to the user) ++ * - ORP ++ * - DCA/order-preservation (see note in "pull dequeues") ++ * - enqueue consumption interrupts ++ * ++ * Return 0 for successful enqueue, or -EBUSY if the enqueue ring is not ready, ++ * or -ENODEV if there is no dpio service. ++ */ ++int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, ++ uint32_t fqid, ++ const struct dpaa2_fd *fd); ++ ++/** ++ * dpaa2_io_service_enqueue_qd() - Enqueue a frame to a QD. ++ * @d: the given DPIO service. ++ * @qdid: the given queuing destination id. ++ * @prio: the given queuing priority. ++ * @qdbin: the given queuing destination bin. ++ * @fd: the frame descriptor which is enqueued. ++ * ++ * This definition bypasses some features that are not expected to be priority-1 ++ * features, and may not be needed at all via current assumptions (QBMan's ++ * feature set is wider than the MC object model is intendeding to support, ++ * initially at least). Plus, keeping them out (for now) keeps the API view ++ * simpler. Missing features are; ++ * - enqueue confirmation (results DMA'd back to the user) ++ * - ORP ++ * - DCA/order-preservation (see note in "pull dequeues") ++ * - enqueue consumption interrupts ++ * ++ * Return 0 for successful enqueue, or -EBUSY if the enqueue ring is not ready, ++ * or -ENODEV if there is no dpio service. ++ */ ++int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, ++ uint32_t qdid, uint8_t prio, uint16_t qdbin, ++ const struct dpaa2_fd *fd); ++ ++/*******************/ ++/* Buffer handling */ ++/*******************/ ++ ++/** ++ * dpaa2_io_service_release() - Release buffers to a buffer pool. ++ * @d: the given DPIO object. ++ * @bpid: the buffer pool id. ++ * @buffers: the buffers to be released. ++ * @num_buffers: the number of the buffers to be released. ++ * ++ * Return 0 for success, and negative error code for failure. ++ */ ++int dpaa2_io_service_release(struct dpaa2_io *d, ++ uint32_t bpid, ++ const uint64_t *buffers, ++ unsigned int num_buffers); ++ ++/** ++ * dpaa2_io_service_acquire() - Acquire buffers from a buffer pool. ++ * @d: the given DPIO object. ++ * @bpid: the buffer pool id. ++ * @buffers: the buffer addresses for acquired buffers. ++ * @num_buffers: the expected number of the buffers to acquire. ++ * ++ * Return a negative error code if the command failed, otherwise it returns ++ * the number of buffers acquired, which may be less than the number requested. ++ * Eg. if the buffer pool is empty, this will return zero. ++ */ ++int dpaa2_io_service_acquire(struct dpaa2_io *d, ++ uint32_t bpid, ++ uint64_t *buffers, ++ unsigned int num_buffers); ++ ++/***************/ ++/* DPIO stores */ ++/***************/ ++ ++/* These are reusable memory blocks for retrieving dequeue results into, and to ++ * assist with parsing those results once they show up. They also hide the ++ * details of how to use "tokens" to make detection of DMA results possible (ie. ++ * comparing memory before the DMA and after it) while minimising the needless ++ * clearing/rewriting of those memory locations between uses. ++ */ ++ ++/** ++ * dpaa2_io_store_create() - Create the dma memory storage for dequeue ++ * result. ++ * @max_frames: the maximum number of dequeued result for frames, must be <= 16. ++ * @dev: the device to allow mapping/unmapping the DMAable region. ++ * ++ * Constructor - max_frames must be <= 16. The user provides the ++ * device struct to allow mapping/unmapping of the DMAable region. Area for ++ * storage will be allocated during create. The size of this storage is ++ * "max_frames*sizeof(struct dpaa2_dq)". The 'dpaa2_io_store' returned is a ++ * wrapper structure allocated within the DPIO code, which owns and manages ++ * allocated store. ++ * ++ * Return dpaa2_io_store struct for successfuly created storage memory, or NULL ++ * if not getting the stroage for dequeue result in create API. ++ */ ++struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, ++ struct device *dev); ++ ++/** ++ * dpaa2_io_store_destroy() - Destroy the dma memory storage for dequeue ++ * result. ++ * @s: the storage memory to be destroyed. ++ * ++ * Frees to specified storage memory. ++ */ ++void dpaa2_io_store_destroy(struct dpaa2_io_store *s); ++ ++/** ++ * dpaa2_io_store_next() - Determine when the next dequeue result is available. ++ * @s: the dpaa2_io_store object. ++ * @is_last: indicate whether this is the last frame in the pull command. ++ * ++ * Once dpaa2_io_store has been passed to a function that performs dequeues to ++ * it, like dpaa2_ni_rx(), this function can be used to determine when the next ++ * frame result is available. Once this function returns non-NULL, a subsequent ++ * call to it will try to find the *next* dequeue result. ++ * ++ * Note that if a pull-dequeue has a null result because the target FQ/channel ++ * was empty, then this function will return NULL rather than expect the caller ++ * to always check for this on his own side. As such, "is_last" can be used to ++ * differentiate between "end-of-empty-dequeue" and "still-waiting". ++ * ++ * Return dequeue result for a valid dequeue result, or NULL for empty dequeue. ++ */ ++struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last); ++ ++#ifdef CONFIG_FSL_QBMAN_DEBUG ++/** ++ * dpaa2_io_query_fq_count() - Get the frame and byte count for a given fq. ++ * @d: the given DPIO object. ++ * @fqid: the id of frame queue to be queried. ++ * @fcnt: the queried frame count. ++ * @bcnt: the queried byte count. ++ * ++ * Knowing the FQ count at run-time can be useful in debugging situations. ++ * The instantaneous frame- and byte-count are hereby returned. ++ * ++ * Return 0 for a successful query, and negative error code if query fails. ++ */ ++int dpaa2_io_query_fq_count(struct dpaa2_io *d, uint32_t fqid, ++ uint32_t *fcnt, uint32_t *bcnt); ++ ++/** ++ * dpaa2_io_query_bp_count() - Query the number of buffers currenty in a ++ * buffer pool. ++ * @d: the given DPIO object. ++ * @bpid: the index of buffer pool to be queried. ++ * @num: the queried number of buffers in the buffer pool. ++ * ++ * Return 0 for a sucessful query, and negative error code if query fails. ++ */ ++int dpaa2_io_query_bp_count(struct dpaa2_io *d, uint32_t bpid, ++ uint32_t *num); ++#endif ++#endif /* __FSL_DPAA2_IO_H */ |