mbox series

[v8,00/18] dmaengine/soc: Add Texas Instruments UDMA support

Message ID 20191223110458.30766-1-peter.ujfalusi@ti.com
Headers show
Series dmaengine/soc: Add Texas Instruments UDMA support | expand

Message

Peter Ujfalusi Dec. 23, 2019, 11:04 a.m. UTC
Hi,

Vinod, Nishanth, Tero, Santosh: the ti_sci patch in this series was sent
upstream over a month ago:
https://lore.kernel.org/lkml/20191025084715.25098-1-peter.ujfalusi@ti.com/

I'm still waiting on it's fate (Tero has given his r-b).
The ti_sci patch did not made it to 5.5-rc1, but I included it in the series and
let the maintainers decide if it can go via DMAengine for 5.6 or to later
releases (5.6 probably for the ti_sci and 5.7 for the UDMA driver patch).

Patch 1-11 is the initial DMA support which can be applied as it is.
Patch 12-13 adds support for j721e tdtype for ti_sci and the UDMAP driver
Patch 14-15 exports the currently unexported functions:
		devm_ti_sci_get_of_resource()
		of_msi_get_domain()
Patch 16-18 makes the ringacc, DMAengine and glue layer buildable as module.

Changes since v7:
(https://lore.kernel.org/lkml/20191209094332.4047-1-peter.ujfalusi@ti.com/)

- Added Tested-by from Keerthy
- Added Reviewed-by from Grygorii

- DMAengine:
 - helper function for converting direction value to text

- DMAengine metadata support:
 - Improve documentation and comments inside the code

- ringacc driver:
 - Do not allow it to be built as a module for now as two exports are missing
   from kernel:
    - devm_ti_sci_get_of_resource()
    - of_msi_get_domain()

- cppi5 header
 - Remove cppi5_hdesc_get_psdata32()
 - Define for default flow: CPPI5_INFO1_DESC_FLOWID_DEFAULT

- UDMAP DT bindings:
 - Typo fixes.

- UDMAP DMAengine driver:
 - Do not allow it to be built as a module for now as two exports are missing
   from kernel:
    - devm_ti_sci_get_of_resource()
    - of_msi_get_domain()
 - channel (runtime) configuration moved to a struct and using memset/memcpy on
   it when needed.
 - Corrected the udma_tx_status for pause reporting
 - Use GFP_NOWAIT in prep callback paths
 - Magic number for packet length changed to >= SZ_4M
 - Use define for default flow number (0x3fff)
 - Use the generic of_msi_get_domain()
 - No casting from void *

- UDMAP glue layer:
 - Do not allow it to be built as a module for now as two exports are missing
   from kernel:
    - devm_ti_sci_get_of_resource()
    - of_msi_get_domain()

Changes since v6:
(https://patchwork.kernel.org/project/linux-dmaengine/list/?series=209455&state=*)

- UDMAP DMAengine driver:
 - Squashed the split patches
 - Squashed the early TX completion handling update
   (https://patchwork.kernel.org/project/linux-dmaengine/list/?series=210713&state=*)
 - Hard reset fix for RX channels to avoid channel lockdown
 - Correct completed descriptor's residue value

Changes since v5:
(https://patchwork.kernel.org/project/linux-dmaengine/list/?series=201051&state=*)
- Based on 5.4

- cppi5 header
 - clear the bits before setting new value with '|='

- UDMAP DT bindings:
 - valid compatibles as single enum list

- UDMAP DMAengine driver:
 - Fix udma_is_chan_running()
 - Use flags for acc32, burst support instead of a bool in udma_match_data
   struct
 - TDTYPE handling (teardown completion handling for j721e) is moved to separate
   patch as the tisci core patch has not moved for over a month.
   Both ti_sci and the iterative patch to udma is included in the series.

Changes since v4
(https://patchwork.kernel.org/project/linux-dmaengine/list/?series=196619&state=*)
- Based on 5.4-rc7

- ringacc DT bindings:
 - clarify the meaning of ti,sci-dev-id

- ringacc driver:
 - Remove 'default y' from Kconfig
 - Fix struct comments
 - Move try_module_get() earlier in k3_ringacc_request_ring()

- PSI-L thread database:
 - Add kernel style struct/enum documentation
 - Add missing thread description for sa2ul second interface
 - Change EXPORT_SYMBOL to EXPORT_SYMBOL_GPL

- UDMAP DT bindings:
 - move to dual license
 - change compatible from const to enum
 - items dropped for ti,sci-rm-ranges-*
 - description text moved from literal block when it is sensible
 - example fixed to compile cleanly
  - added parent to provide correct address-cells
  - navss is moved to simple-mfd from simple-bus

- UDMAP DMAengine driver:
 - move fd_ring/r_ring under rflow
 - get rid of unused iomem for rflows
 - Remove 'default y' from Kconfig
 - Use defines for rflow src/dst tag selection
 - Merge the udma_ring_callback() and udma_tr_event_callback() to their
   corresponding interrupt handler
 - Create new defines for tx/rx channel's tisci valid parameter flags
 - Remove re-initialization to 0 of tisci request struct members
 - Make sure that vchan tasklets are also stopped when removing the module
 - Additional checkpatch --strict fixes when it made sense
  - make W=1 was clean

- UDMAP glue layer:
 - Remove 'default y' from Kconfig
 - commit message update for features needing the glue layer

Changes since v3
(https://patchwork.kernel.org/project/linux-dmaengine/list/?series=180679&state=*):
- Based on 5.4-rc5
- Fixed typos pointed out by Tero
- Added reviewed-by tags from Tero

- ring accelerator driver
 - TODO_GS is removed from the header
 - pm_runtime removed as NAVSS and it's components are always on
 - Check validity of Message mode setup (element size > 8 bytes must use proxy)

- cppi5 header
 - add commit message

- UDMAP DT bindings
 - Drop the psil-config node use on the remote PSI-L side and use only one cell
   which is the remote threadID:

     dmas = <&main_udmap 0xc400>, <&main_udmap 0x4400>;
     dma-names = "tx", "rx";

 - The PSI-L thread configuration description is moved to kernel as a new module:
   k3-psil/k3-psil-am654/k3-psil-j721e
 - ti,psil-base has been removed and moved to kernel
 - removed the no longer needed dt-bindings/dma/k3-udma.h
 - Convert the document to schema (yaml)

- NEW PSI-L endpoint configuration database
 - a simple database holding the remote end's configuration needed for UDMAP
   configuration. All previous parameters from DT has been moved here and merged
   with the linux only tr mode channel flag.
 - Client drivers can update the remote endpoint configuration as it can be
   different based on system configuration and the endpoint itself is under the
   control of the peripheral driver.
 - database for am654 and j721e

- UDMAP DMAengine driver
 - pm_runtime removed as NAVSS and it's components are always on
 - rchan_oes_offset added to MSI dommain allocation
 - Use the new PSI-L endpoint database for UDMAP configuration
 - Support for waiting for PDMA teardown completion on j721e instead of
   returning right away. depends on:
   https://lkml.org/lkml/2019/10/25/189
   Not included in this series, but it is in the branch I have prepared.
 - psil-base is moved from DT to be part of udma_match_data
 - tr_thread maps is removed and using the PSI-L endpoint configuration for it

- UDMAP glue layer
 - pm_runtime removed as NAVSS and it's components are always on
 - Use the new PSI-L endpoint database for UDMAP configuration

Changes since v2
(https://patchwork.kernel.org/project/linux-dmaengine/list/?series=152609&state=*)
- Based on 5.4-rc1
- Support for Flow only data transfer for the glue layer

- cppi5 header
 - comments converted to kernel-doc style
 - Remove the excessive WARN_ONs and rely on the user for sanity
 - new macro for checking TearDown Completion Message

- ring accelerator driver
 - fixed up th commit message (SoB, TI-SCI)
 - fixed ring reset
 - CONFIG_TI_K3_RINGACC_DEBUG is removed along with the dbg_write/read functions
   and use dev_dbg()
 - k3_ringacc_ring_dump() is moved to static
 - step numbering removed from k3_ringacc_ring_reset_dma()
 - Add clarification comment for shared ring usage in k3_ringacc_ring_cfg()
 - Magic shift values in k3_ringacc_ring_cfg_proxy() got defined
 - K3_RINGACC_RING_MODE_QM is removed as it is not supported

- UDMAP DT bindings
 - Fix property prefixing: s/pdma,/ti,pdma-
 - Add ti,notdpkt property to suppress teardown completion message on tchan
 - example updated accordingly

- UDMAP DMAengine driver
 - Change __raw_readl/writel to readl/writel
 - Split up the udma_tisci_channel_config() into m2m, tx and rx tisci
   configuration functions for clarity
 - DT bindings change: s/pdma,/ti,pdma-
 - Cleanup of udma_tx_status():
  - residue calculation fix for m2m
  - no need to read packet counter as it is not used
  - peer byte counter only available in PDMAs
  - Proper locking to avoid race with interrupt handler (polled m2m fix)
 - Support for ti,notdpkt
 - RFLOW management rework to support data movement without channel:
  - the channel is not controlled by Linux but other core and we only have
    rflows and rings to do the DMA transfers.
    This mode is only supported by the Glue layer for now.

- UDMAP glue layer
 - Debug print improvements
 - Support for rflow/ring only data movement

Changes since v1
(https://patchwork.kernel.org/project/linux-dmaengine/list/?series=114105&state=*)
- Added support for j721e
- Based on 5.3-rc2
- dropped ti_sci API patch for RM management as it is already upstream
- dropped dmadev_get_slave_channel() patch, using __dma_request_channel()
- Added Rob's Reviewed-by to ringacc DT binding document patch
- DT bindings changes:
 - linux,udma-mode is gone, I have a simple lookup table in the driver to flag
   TR channels.
 - Support for j721e
- Fix bug in of_node_put() handling in xlate function

Changes since RFC (https://patchwork.kernel.org/cover/10612465/):
- Based on linux-next (20190506) which now have the ti_sci interrupt support
- The series can be applied and the UDMA via DMAengine API will be functional
- Included in the series: ti_sci Resource management API, cppi5 header and
  driver for the ring accelerator.
- The DMAengine core patches have been updated as per the review comments for
  earlier submittion.
- The DMAengine driver patch is artificially split up to 6 smaller patches

The k3-udma driver implements the Data Movement Architecture described in
AM65x TRM (http://www.ti.com/lit/pdf/spruid7) and
j721e TRM (http://www.ti.com/lit/pdf/spruil1)

This DMA architecture is a big departure from 'traditional' architecture where
we had either EDMA or sDMA as system DMA.

Packet DMAs were used as dedicated DMAs to service only networking (Kesytone2)
or USB (am335x) while other peripherals were serviced by EDMA.

In AM65x/j721e the UDMA (Unified DMA) is used for all data movment within the
SoC, tasked to service all peripherals (UART, McSPI, McASP, networking, etc). 

The NAVSS/UDMA is built around CPPI5 (Communications Port Programming Interface)
and it supports Packet mode (similar to CPPI4.1 in Keystone2 for networking) and
TR mode (similar to EDMA descriptor).
The data movement is done within a PSI-L fabric, peripherals (including the
UDMA-P) are not addressed by their I/O register as with traditional DMAs but
with their PSI-L thread ID.

In AM65x/j721e we have two main type of peripherals:
Legacy: McASP, McSPI, UART, etc.
 to provide connectivity they are serviced by PDMA (Peripheral DMA)
 PDMA threads are locked to service a given peripheral, for example PSI-L thread
 0x4400/0xc400 is to service McASP0 rx/tx.
 The PDMa configuration can be done via the UDMA Real Time Peer registers.
Native: Networking, security accelerator
 these peripherals have native support for PSI-L.

To be able to use the DMA the following generic steps need to be taken:
- configure a DMA channel (tchan for TX, rchan for RX)
 - channel mode: Packet or TR mode
 - for memcpy a tchan and rchan pair is used.
 - for packet mode RX we also need to configure a receive flow to configure the
   packet receiption
- the source and destination threads must be paired
- at minimum one pair of rings need to be configured:
 - tx: transfer ring and transfer completion ring
 - rx: free descriptor ring and receive ring
- two interrupts: UDMA-P channel interrupt and ring interrupt for tc_ring/r_ring
 - If the channel is in packet mode or configured to memcpy then we only need
   one interrupt from the ring, events from UDMAP is not used.

When the channel setup is completed we only interract with the rings:
- TX: push a descriptor to t_ring and wait for it to be pushed to the tc_ring by
  the UDMA-P
- RX: push a descriptor to the fd_ring and waith for UDMA-P to push it back to
  the r_ring.

Since we have FIFOs in the DMA fabric (UDMA-P, PSI-L and PDMA) which was not the
case in previous DMAs we need to report the amount of data held in these FIFOs
to clients (delay calculation for ALSA, UART FIFO flush support).

Metadata support:
DMAengine user driver was posted upstream based/tested on the v1 of the UDMA
series: https://lkml.org/lkml/2019/6/28/20
SA2UL is using the metadata DMAengine API.

Note on the last patch:
In Keystone2 the networking had dedicated DMA (packet DMA) which is not the case
anymore and the DMAengine API currently missing support for the features we
would need to support networking, things like
- support for receive descriptor 'classification'
 - we need to support several receive queues for a channel.
 - the queues are used for packet priority handling for example, but they can be
   used to have pools of descriptors for different sizes.
- out of order completion of descriptors on a channel
 - when we have several queues to handle different priority packets the
   descriptors will be completed 'out-of-order'
- NAPI type of operation (polling instead of interrupt driven transfer)
 - without this we can not sustain gigabit speeds and we need to support NAPI
 - not to limit this to networking, but other high performance operations

It is my intention to work on these to be able to remove the 'glue' layer and
switch to DMAengine API - or have an API aside of DMAengine to have generic way
to support networking, but given how controversial and not trivial these changes
are we need something to support networking.

The series (+DT patches to enabled DMA on AM65x and j721e) on top of 5.5-rc3 is
available:
https://github.com/omap-audio/linux-audio.git peter/udma/series_v8-5.5-rc3

Regards,
Peter
---
Grygorii Strashko (3):
  bindings: soc: ti: add documentation for k3 ringacc
  soc: ti: k3: add navss ringacc driver
  dmaengine: ti: k3-udma: Add glue layer for non DMAengine users

Matthias Brugger (1):
  of: irq: Export of_msi_get_domain

Peter Ujfalusi (14):
  dmaengine: doc: Add sections for per descriptor metadata support
  dmaengine: Add metadata_ops for dma_async_tx_descriptor
  dmaengine: Add support for reporting DMA cached data amount
  dmaengine: Add helper function to convert direction value to text
  dmaengine: ti: Add cppi5 header for K3 NAVSS/UDMA
  dmaengine: ti: k3 PSI-L remote endpoint configuration
  dt-bindings: dma: ti: Add document for K3 UDMA
  dmaengine: ti: New driver for K3 UDMA
  firmware: ti_sci: rm: Add support for tx_tdtype parameter for tx
    channel
  dmaengine: ti: k3-udma: Wait for peer teardown completion if supported
  firmware: ti_sci: Export devm_ti_sci_get_of_resource for modules
  dmaengine: ti: k3-udma: Allow the driver to be built as module
  dmaengine: ti: k3-udma-glue: Allow the driver to be built as module
  soc: ti: k3-ringacc: Allow the driver to be built as module

 .../devicetree/bindings/dma/ti/k3-udma.yaml   |  184 +
 .../devicetree/bindings/soc/ti/k3-ringacc.txt |   59 +
 Documentation/driver-api/dmaengine/client.rst |   87 +
 .../driver-api/dmaengine/provider.rst         |   48 +
 drivers/dma/dmaengine.c                       |   73 +
 drivers/dma/dmaengine.h                       |    8 +
 drivers/dma/ti/Kconfig                        |   24 +
 drivers/dma/ti/Makefile                       |    3 +
 drivers/dma/ti/k3-psil-am654.c                |  175 +
 drivers/dma/ti/k3-psil-j721e.c                |  222 ++
 drivers/dma/ti/k3-psil-priv.h                 |   39 +
 drivers/dma/ti/k3-psil.c                      |   93 +
 drivers/dma/ti/k3-udma-glue.c                 | 1200 ++++++
 drivers/dma/ti/k3-udma-private.c              |  133 +
 drivers/dma/ti/k3-udma.c                      | 3465 +++++++++++++++++
 drivers/dma/ti/k3-udma.h                      |  151 +
 drivers/firmware/ti_sci.c                     |    2 +
 drivers/firmware/ti_sci.h                     |    7 +
 drivers/of/irq.c                              |    1 +
 drivers/soc/ti/Kconfig                        |   11 +
 drivers/soc/ti/Makefile                       |    1 +
 drivers/soc/ti/k3-ringacc.c                   | 1180 ++++++
 include/linux/dma/k3-psil.h                   |   71 +
 include/linux/dma/k3-udma-glue.h              |  134 +
 include/linux/dma/ti-cppi5.h                  | 1059 +++++
 include/linux/dmaengine.h                     |  133 +
 include/linux/soc/ti/k3-ringacc.h             |  244 ++
 include/linux/soc/ti/ti_sci_protocol.h        |    2 +
 28 files changed, 8809 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/ti/k3-udma.yaml
 create mode 100644 Documentation/devicetree/bindings/soc/ti/k3-ringacc.txt
 create mode 100644 drivers/dma/ti/k3-psil-am654.c
 create mode 100644 drivers/dma/ti/k3-psil-j721e.c
 create mode 100644 drivers/dma/ti/k3-psil-priv.h
 create mode 100644 drivers/dma/ti/k3-psil.c
 create mode 100644 drivers/dma/ti/k3-udma-glue.c
 create mode 100644 drivers/dma/ti/k3-udma-private.c
 create mode 100644 drivers/dma/ti/k3-udma.c
 create mode 100644 drivers/dma/ti/k3-udma.h
 create mode 100644 drivers/soc/ti/k3-ringacc.c
 create mode 100644 include/linux/dma/k3-psil.h
 create mode 100644 include/linux/dma/k3-udma-glue.h
 create mode 100644 include/linux/dma/ti-cppi5.h
 create mode 100644 include/linux/soc/ti/k3-ringacc.h

-- 
Peter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

Comments

Peter Ujfalusi Dec. 23, 2019, 11:36 a.m. UTC | #1
Hi Matthias,

On 23/12/2019 13.04, Peter Ujfalusi wrote:
> From: Matthias Brugger <matthias.bgg@gmail.com>

> 

> Export of_mis_get_domain to enable it for users from outside.


FYI, I have picked this old patch from you. I can not find the history
why it is not in the kernel...

> Signed-off-by: Matthias Brugger <mbrugger@suse.com>

> Acked-by: Rob Herring <robh@kernel.org>

> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>

> ---

>  drivers/of/irq.c | 1 +

>  1 file changed, 1 insertion(+)

> 

> diff --git a/drivers/of/irq.c b/drivers/of/irq.c

> index a296eaf52a5b..73017506ef00 100644

> --- a/drivers/of/irq.c

> +++ b/drivers/of/irq.c

> @@ -673,6 +673,7 @@ struct irq_domain *of_msi_get_domain(struct device *dev,

>  

>  	return NULL;

>  }

> +EXPORT_SYMBOL_GPL(of_msi_get_domain);

>  

>  /**

>   * of_msi_configure - Set the msi_domain field of a device

> 


- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
Peter Ujfalusi Dec. 23, 2019, 11:38 a.m. UTC | #2
Hi Santosh,

On 23/12/2019 13.04, Peter Ujfalusi wrote:
> From: Grygorii Strashko <grygorii.strashko@ti.com>

> 

> The Ring Accelerator (RINGACC or RA) provides hardware acceleration to

> enable straightforward passing of work between a producer and a consumer.

> There is one RINGACC module per NAVSS on TI AM65x SoCs.

> 

> The RINGACC converts constant-address read and write accesses to equivalent

> read or write accesses to a circular data structure in memory. The RINGACC

> eliminates the need for each DMA controller which needs to access ring

> elements from having to know the current state of the ring (base address,

> current offset). The DMA controller performs a read or write access to a

> specific address range (which maps to the source interface on the RINGACC)

> and the RINGACC replaces the address for the transaction with a new address

> which corresponds to the head or tail element of the ring (head for reads,

> tail for writes). Since the RINGACC maintains the state, multiple DMA

> controllers or channels are allowed to coherently share the same rings as

> applicable. The RINGACC is able to place data which is destined towards

> software into cached memory directly.

> 

> Supported ring modes:

> - Ring Mode

> - Messaging Mode

> - Credentials Mode

> - Queue Manager Mode

> 

> TI-SCI integration:

> 

> Texas Instrument's System Control Interface (TI-SCI) Message Protocol now

> has control over Ringacc module resources management (RM) and Rings

> configuration.

> 

> The corresponding support of TI-SCI Ringacc module RM protocol

> introduced as option through DT parameters:

> - ti,sci: phandle on TI-SCI firmware controller DT node

> - ti,sci-dev-id: TI-SCI device identifier as per TI-SCI firmware spec

> 

> if both parameters present - Ringacc driver will configure/free/reset Rings

> using TI-SCI Message Ringacc RM Protocol.

> 

> The Ringacc driver manages Rings allocation by itself now and requests

> TI-SCI firmware to allocate and configure specific Rings only. It's done

> this way because, Linux driver implements two stage Rings allocation and

> configuration (allocate ring and configure ring) while TI-SCI Message

> Protocol supports only one combined operation (allocate+configure).

> 

> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>

> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>

> Reviewed-by: Tero Kristo <t-kristo@ti.com>

> Tested-by: Keerthy <j-keerthy@ti.com>


Can you please giver your Acked-by for the ringacc patches if they are
still OK from your point of view as you had offered to take them before
I got comments from Lokesh.

Thank you,
- Péter

> ---

>  drivers/soc/ti/Kconfig            |   11 +

>  drivers/soc/ti/Makefile           |    1 +

>  drivers/soc/ti/k3-ringacc.c       | 1157 +++++++++++++++++++++++++++++

>  include/linux/soc/ti/k3-ringacc.h |  244 ++++++

>  4 files changed, 1413 insertions(+)

>  create mode 100644 drivers/soc/ti/k3-ringacc.c

>  create mode 100644 include/linux/soc/ti/k3-ringacc.h

> 

> diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig

> index cf545f428d03..4486e055794c 100644

> --- a/drivers/soc/ti/Kconfig

> +++ b/drivers/soc/ti/Kconfig

> @@ -80,6 +80,17 @@ config TI_SCI_PM_DOMAINS

>  	  called ti_sci_pm_domains. Note this is needed early in boot before

>  	  rootfs may be available.

>  

> +config TI_K3_RINGACC

> +	bool "K3 Ring accelerator Sub System"

> +	depends on ARCH_K3 || COMPILE_TEST

> +	depends on TI_SCI_INTA_IRQCHIP

> +	help

> +	  Say y here to support the K3 Ring accelerator module.

> +	  The Ring Accelerator (RINGACC or RA)  provides hardware acceleration

> +	  to enable straightforward passing of work between a producer

> +	  and a consumer. There is one RINGACC module per NAVSS on TI AM65x SoCs

> +	  If unsure, say N.

> +

>  endif # SOC_TI

>  

>  config TI_SCI_INTA_MSI_DOMAIN

> diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile

> index 788b5cd1e180..bec827937a5f 100644

> --- a/drivers/soc/ti/Makefile

> +++ b/drivers/soc/ti/Makefile

> @@ -10,3 +10,4 @@ obj-$(CONFIG_ARCH_OMAP2PLUS)		+= omap_prm.o

>  obj-$(CONFIG_WKUP_M3_IPC)		+= wkup_m3_ipc.o

>  obj-$(CONFIG_TI_SCI_PM_DOMAINS)		+= ti_sci_pm_domains.o

>  obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN)	+= ti_sci_inta_msi.o

> +obj-$(CONFIG_TI_K3_RINGACC)		+= k3-ringacc.o

> diff --git a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c

> new file mode 100644

> index 000000000000..5fb2ee2ac978

> --- /dev/null

> +++ b/drivers/soc/ti/k3-ringacc.c

> @@ -0,0 +1,1157 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * TI K3 NAVSS Ring Accelerator subsystem driver

> + *

> + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com

> + */

> +

> +#include <linux/dma-mapping.h>

> +#include <linux/io.h>

> +#include <linux/init.h>

> +#include <linux/of.h>

> +#include <linux/platform_device.h>

> +#include <linux/soc/ti/k3-ringacc.h>

> +#include <linux/soc/ti/ti_sci_protocol.h>

> +#include <linux/soc/ti/ti_sci_inta_msi.h>

> +#include <linux/of_irq.h>

> +#include <linux/irqdomain.h>

> +

> +static LIST_HEAD(k3_ringacc_list);

> +static DEFINE_MUTEX(k3_ringacc_list_lock);

> +

> +#define K3_RINGACC_CFG_RING_SIZE_ELCNT_MASK		GENMASK(19, 0)

> +

> +/**

> + * struct k3_ring_rt_regs - The RA realtime Control/Status Registers region

> + *

> + * @resv_16: Reserved

> + * @db: Ring Doorbell Register

> + * @resv_4: Reserved

> + * @occ: Ring Occupancy Register

> + * @indx: Ring Current Index Register

> + * @hwocc: Ring Hardware Occupancy Register

> + * @hwindx: Ring Hardware Current Index Register

> + */

> +struct k3_ring_rt_regs {

> +	u32	resv_16[4];

> +	u32	db;

> +	u32	resv_4[1];

> +	u32	occ;

> +	u32	indx;

> +	u32	hwocc;

> +	u32	hwindx;

> +};

> +

> +#define K3_RINGACC_RT_REGS_STEP	0x1000

> +

> +/**

> + * struct k3_ring_fifo_regs - The Ring Accelerator Queues Registers region

> + *

> + * @head_data: Ring Head Entry Data Registers

> + * @tail_data: Ring Tail Entry Data Registers

> + * @peek_head_data: Ring Peek Head Entry Data Regs

> + * @peek_tail_data: Ring Peek Tail Entry Data Regs

> + */

> +struct k3_ring_fifo_regs {

> +	u32	head_data[128];

> +	u32	tail_data[128];

> +	u32	peek_head_data[128];

> +	u32	peek_tail_data[128];

> +};

> +

> +/**

> + * struct k3_ringacc_proxy_gcfg_regs - RA Proxy Global Config MMIO Region

> + *

> + * @revision: Revision Register

> + * @config: Config Register

> + */

> +struct k3_ringacc_proxy_gcfg_regs {

> +	u32	revision;

> +	u32	config;

> +};

> +

> +#define K3_RINGACC_PROXY_CFG_THREADS_MASK		GENMASK(15, 0)

> +

> +/**

> + * struct k3_ringacc_proxy_target_regs - Proxy Datapath MMIO Region

> + *

> + * @control: Proxy Control Register

> + * @status: Proxy Status Register

> + * @resv_512: Reserved

> + * @data: Proxy Data Register

> + */

> +struct k3_ringacc_proxy_target_regs {

> +	u32	control;

> +	u32	status;

> +	u8	resv_512[504];

> +	u32	data[128];

> +};

> +

> +#define K3_RINGACC_PROXY_TARGET_STEP	0x1000

> +#define K3_RINGACC_PROXY_NOT_USED	(-1)

> +

> +enum k3_ringacc_proxy_access_mode {

> +	PROXY_ACCESS_MODE_HEAD = 0,

> +	PROXY_ACCESS_MODE_TAIL = 1,

> +	PROXY_ACCESS_MODE_PEEK_HEAD = 2,

> +	PROXY_ACCESS_MODE_PEEK_TAIL = 3,

> +};

> +

> +#define K3_RINGACC_FIFO_WINDOW_SIZE_BYTES  (512U)

> +#define K3_RINGACC_FIFO_REGS_STEP	0x1000

> +#define K3_RINGACC_MAX_DB_RING_CNT    (127U)

> +

> +struct k3_ring_ops {

> +	int (*push_tail)(struct k3_ring *ring, void *elm);

> +	int (*push_head)(struct k3_ring *ring, void *elm);

> +	int (*pop_tail)(struct k3_ring *ring, void *elm);

> +	int (*pop_head)(struct k3_ring *ring, void *elm);

> +};

> +

> +/**

> + * struct k3_ring - RA Ring descriptor

> + *

> + * @rt: Ring control/status registers

> + * @fifos: Ring queues registers

> + * @proxy: Ring Proxy Datapath registers

> + * @ring_mem_dma: Ring buffer dma address

> + * @ring_mem_virt: Ring buffer virt address

> + * @ops: Ring operations

> + * @size: Ring size in elements

> + * @elm_size: Size of the ring element

> + * @mode: Ring mode

> + * @flags: flags

> + * @free: Number of free elements

> + * @occ: Ring occupancy

> + * @windex: Write index (only for @K3_RINGACC_RING_MODE_RING)

> + * @rindex: Read index (only for @K3_RINGACC_RING_MODE_RING)

> + * @ring_id: Ring Id

> + * @parent: Pointer on struct @k3_ringacc

> + * @use_count: Use count for shared rings

> + * @proxy_id: RA Ring Proxy Id (only if @K3_RINGACC_RING_USE_PROXY)

> + */

> +struct k3_ring {

> +	struct k3_ring_rt_regs __iomem *rt;

> +	struct k3_ring_fifo_regs __iomem *fifos;

> +	struct k3_ringacc_proxy_target_regs  __iomem *proxy;

> +	dma_addr_t	ring_mem_dma;

> +	void		*ring_mem_virt;

> +	struct k3_ring_ops *ops;

> +	u32		size;

> +	enum k3_ring_size elm_size;

> +	enum k3_ring_mode mode;

> +	u32		flags;

> +#define K3_RING_FLAG_BUSY	BIT(1)

> +#define K3_RING_FLAG_SHARED	BIT(2)

> +	u32		free;

> +	u32		occ;

> +	u32		windex;

> +	u32		rindex;

> +	u32		ring_id;

> +	struct k3_ringacc	*parent;

> +	u32		use_count;

> +	int		proxy_id;

> +};

> +

> +/**

> + * struct k3_ringacc - Rings accelerator descriptor

> + *

> + * @dev: pointer on RA device

> + * @proxy_gcfg: RA proxy global config registers

> + * @proxy_target_base: RA proxy datapath region

> + * @num_rings: number of ring in RA

> + * @rings_inuse: bitfield for ring usage tracking

> + * @rm_gp_range: general purpose rings range from tisci

> + * @dma_ring_reset_quirk: DMA reset w/a enable

> + * @num_proxies: number of RA proxies

> + * @proxy_inuse: bitfield for proxy usage tracking

> + * @rings: array of rings descriptors (struct @k3_ring)

> + * @list: list of RAs in the system

> + * @req_lock: protect rings allocation

> + * @tisci: pointer ti-sci handle

> + * @tisci_ring_ops: ti-sci rings ops

> + * @tisci_dev_id: ti-sci device id

> + */

> +struct k3_ringacc {

> +	struct device *dev;

> +	struct k3_ringacc_proxy_gcfg_regs __iomem *proxy_gcfg;

> +	void __iomem *proxy_target_base;

> +	u32 num_rings; /* number of rings in Ringacc module */

> +	unsigned long *rings_inuse;

> +	struct ti_sci_resource *rm_gp_range;

> +

> +	bool dma_ring_reset_quirk;

> +	u32 num_proxies;

> +	unsigned long *proxy_inuse;

> +

> +	struct k3_ring *rings;

> +	struct list_head list;

> +	struct mutex req_lock; /* protect rings allocation */

> +

> +	const struct ti_sci_handle *tisci;

> +	const struct ti_sci_rm_ringacc_ops *tisci_ring_ops;

> +	u32 tisci_dev_id;

> +};

> +

> +static long k3_ringacc_ring_get_fifo_pos(struct k3_ring *ring)

> +{

> +	return K3_RINGACC_FIFO_WINDOW_SIZE_BYTES -

> +	       (4 << ring->elm_size);

> +}

> +

> +static void *k3_ringacc_get_elm_addr(struct k3_ring *ring, u32 idx)

> +{

> +	return (ring->ring_mem_virt + idx * (4 << ring->elm_size));

> +}

> +

> +static int k3_ringacc_ring_push_mem(struct k3_ring *ring, void *elem);

> +static int k3_ringacc_ring_pop_mem(struct k3_ring *ring, void *elem);

> +

> +static struct k3_ring_ops k3_ring_mode_ring_ops = {

> +		.push_tail = k3_ringacc_ring_push_mem,

> +		.pop_head = k3_ringacc_ring_pop_mem,

> +};

> +

> +static int k3_ringacc_ring_push_io(struct k3_ring *ring, void *elem);

> +static int k3_ringacc_ring_pop_io(struct k3_ring *ring, void *elem);

> +static int k3_ringacc_ring_push_head_io(struct k3_ring *ring, void *elem);

> +static int k3_ringacc_ring_pop_tail_io(struct k3_ring *ring, void *elem);

> +

> +static struct k3_ring_ops k3_ring_mode_msg_ops = {

> +		.push_tail = k3_ringacc_ring_push_io,

> +		.push_head = k3_ringacc_ring_push_head_io,

> +		.pop_tail = k3_ringacc_ring_pop_tail_io,

> +		.pop_head = k3_ringacc_ring_pop_io,

> +};

> +

> +static int k3_ringacc_ring_push_head_proxy(struct k3_ring *ring, void *elem);

> +static int k3_ringacc_ring_push_tail_proxy(struct k3_ring *ring, void *elem);

> +static int k3_ringacc_ring_pop_head_proxy(struct k3_ring *ring, void *elem);

> +static int k3_ringacc_ring_pop_tail_proxy(struct k3_ring *ring, void *elem);

> +

> +static struct k3_ring_ops k3_ring_mode_proxy_ops = {

> +		.push_tail = k3_ringacc_ring_push_tail_proxy,

> +		.push_head = k3_ringacc_ring_push_head_proxy,

> +		.pop_tail = k3_ringacc_ring_pop_tail_proxy,

> +		.pop_head = k3_ringacc_ring_pop_head_proxy,

> +};

> +

> +static void k3_ringacc_ring_dump(struct k3_ring *ring)

> +{

> +	struct device *dev = ring->parent->dev;

> +

> +	dev_dbg(dev, "dump ring: %d\n", ring->ring_id);

> +	dev_dbg(dev, "dump mem virt %p, dma %pad\n", ring->ring_mem_virt,

> +		&ring->ring_mem_dma);

> +	dev_dbg(dev, "dump elmsize %d, size %d, mode %d, proxy_id %d\n",

> +		ring->elm_size, ring->size, ring->mode, ring->proxy_id);

> +

> +	dev_dbg(dev, "dump ring_rt_regs: db%08x\n", readl(&ring->rt->db));

> +	dev_dbg(dev, "dump occ%08x\n", readl(&ring->rt->occ));

> +	dev_dbg(dev, "dump indx%08x\n", readl(&ring->rt->indx));

> +	dev_dbg(dev, "dump hwocc%08x\n", readl(&ring->rt->hwocc));

> +	dev_dbg(dev, "dump hwindx%08x\n", readl(&ring->rt->hwindx));

> +

> +	if (ring->ring_mem_virt)

> +		print_hex_dump_debug("dump ring_mem_virt ", DUMP_PREFIX_NONE,

> +				     16, 1, ring->ring_mem_virt, 16 * 8, false);

> +}

> +

> +struct k3_ring *k3_ringacc_request_ring(struct k3_ringacc *ringacc,

> +					int id, u32 flags)

> +{

> +	int proxy_id = K3_RINGACC_PROXY_NOT_USED;

> +

> +	mutex_lock(&ringacc->req_lock);

> +

> +	if (id == K3_RINGACC_RING_ID_ANY) {

> +		/* Request for any general purpose ring */

> +		struct ti_sci_resource_desc *gp_rings =

> +						&ringacc->rm_gp_range->desc[0];

> +		unsigned long size;

> +

> +		size = gp_rings->start + gp_rings->num;

> +		id = find_next_zero_bit(ringacc->rings_inuse, size,

> +					gp_rings->start);

> +		if (id == size)

> +			goto error;

> +	} else if (id < 0) {

> +		goto error;

> +	}

> +

> +	if (test_bit(id, ringacc->rings_inuse) &&

> +	    !(ringacc->rings[id].flags & K3_RING_FLAG_SHARED))

> +		goto error;

> +	else if (ringacc->rings[id].flags & K3_RING_FLAG_SHARED)

> +		goto out;

> +

> +	if (flags & K3_RINGACC_RING_USE_PROXY) {

> +		proxy_id = find_next_zero_bit(ringacc->proxy_inuse,

> +					      ringacc->num_proxies, 0);

> +		if (proxy_id == ringacc->num_proxies)

> +			goto error;

> +	}

> +

> +	if (proxy_id != K3_RINGACC_PROXY_NOT_USED) {

> +		set_bit(proxy_id, ringacc->proxy_inuse);

> +		ringacc->rings[id].proxy_id = proxy_id;

> +		dev_dbg(ringacc->dev, "Giving ring#%d proxy#%d\n", id,

> +			proxy_id);

> +	} else {

> +		dev_dbg(ringacc->dev, "Giving ring#%d\n", id);

> +	}

> +

> +	set_bit(id, ringacc->rings_inuse);

> +out:

> +	ringacc->rings[id].use_count++;

> +	mutex_unlock(&ringacc->req_lock);

> +	return &ringacc->rings[id];

> +

> +error:

> +	mutex_unlock(&ringacc->req_lock);

> +	return NULL;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_request_ring);

> +

> +static void k3_ringacc_ring_reset_sci(struct k3_ring *ring)

> +{

> +	struct k3_ringacc *ringacc = ring->parent;

> +	int ret;

> +

> +	ret = ringacc->tisci_ring_ops->config(

> +			ringacc->tisci,

> +			TI_SCI_MSG_VALUE_RM_RING_COUNT_VALID,

> +			ringacc->tisci_dev_id,

> +			ring->ring_id,

> +			0,

> +			0,

> +			ring->size,

> +			0,

> +			0,

> +			0);

> +	if (ret)

> +		dev_err(ringacc->dev, "TISCI reset ring fail (%d) ring_idx %d\n",

> +			ret, ring->ring_id);

> +}

> +

> +void k3_ringacc_ring_reset(struct k3_ring *ring)

> +{

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return;

> +

> +	ring->occ = 0;

> +	ring->free = 0;

> +	ring->rindex = 0;

> +	ring->windex = 0;

> +

> +	k3_ringacc_ring_reset_sci(ring);

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_reset);

> +

> +static void k3_ringacc_ring_reconfig_qmode_sci(struct k3_ring *ring,

> +					       enum k3_ring_mode mode)

> +{

> +	struct k3_ringacc *ringacc = ring->parent;

> +	int ret;

> +

> +	ret = ringacc->tisci_ring_ops->config(

> +			ringacc->tisci,

> +			TI_SCI_MSG_VALUE_RM_RING_MODE_VALID,

> +			ringacc->tisci_dev_id,

> +			ring->ring_id,

> +			0,

> +			0,

> +			0,

> +			mode,

> +			0,

> +			0);

> +	if (ret)

> +		dev_err(ringacc->dev, "TISCI reconf qmode fail (%d) ring_idx %d\n",

> +			ret, ring->ring_id);

> +}

> +

> +void k3_ringacc_ring_reset_dma(struct k3_ring *ring, u32 occ)

> +{

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return;

> +

> +	if (!ring->parent->dma_ring_reset_quirk)

> +		goto reset;

> +

> +	if (!occ)

> +		occ = readl(&ring->rt->occ);

> +

> +	if (occ) {

> +		u32 db_ring_cnt, db_ring_cnt_cur;

> +

> +		dev_dbg(ring->parent->dev, "%s %u occ: %u\n", __func__,

> +			ring->ring_id, occ);

> +		/* TI-SCI ring reset */

> +		k3_ringacc_ring_reset_sci(ring);

> +

> +		/*

> +		 * Setup the ring in ring/doorbell mode (if not already in this

> +		 * mode)

> +		 */

> +		if (ring->mode != K3_RINGACC_RING_MODE_RING)

> +			k3_ringacc_ring_reconfig_qmode_sci(

> +					ring, K3_RINGACC_RING_MODE_RING);

> +		/*

> +		 * Ring the doorbell 2**22 – ringOcc times.

> +		 * This will wrap the internal UDMAP ring state occupancy

> +		 * counter (which is 21-bits wide) to 0.

> +		 */

> +		db_ring_cnt = (1U << 22) - occ;

> +

> +		while (db_ring_cnt != 0) {

> +			/*

> +			 * Ring the doorbell with the maximum count each

> +			 * iteration if possible to minimize the total

> +			 * of writes

> +			 */

> +			if (db_ring_cnt > K3_RINGACC_MAX_DB_RING_CNT)

> +				db_ring_cnt_cur = K3_RINGACC_MAX_DB_RING_CNT;

> +			else

> +				db_ring_cnt_cur = db_ring_cnt;

> +

> +			writel(db_ring_cnt_cur, &ring->rt->db);

> +			db_ring_cnt -= db_ring_cnt_cur;

> +		}

> +

> +		/* Restore the original ring mode (if not ring mode) */

> +		if (ring->mode != K3_RINGACC_RING_MODE_RING)

> +			k3_ringacc_ring_reconfig_qmode_sci(ring, ring->mode);

> +	}

> +

> +reset:

> +	/* Reset the ring */

> +	k3_ringacc_ring_reset(ring);

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_reset_dma);

> +

> +static void k3_ringacc_ring_free_sci(struct k3_ring *ring)

> +{

> +	struct k3_ringacc *ringacc = ring->parent;

> +	int ret;

> +

> +	ret = ringacc->tisci_ring_ops->config(

> +			ringacc->tisci,

> +			TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER,

> +			ringacc->tisci_dev_id,

> +			ring->ring_id,

> +			0,

> +			0,

> +			0,

> +			0,

> +			0,

> +			0);

> +	if (ret)

> +		dev_err(ringacc->dev, "TISCI ring free fail (%d) ring_idx %d\n",

> +			ret, ring->ring_id);

> +}

> +

> +int k3_ringacc_ring_free(struct k3_ring *ring)

> +{

> +	struct k3_ringacc *ringacc;

> +

> +	if (!ring)

> +		return -EINVAL;

> +

> +	ringacc = ring->parent;

> +

> +	dev_dbg(ring->parent->dev, "flags: 0x%08x\n", ring->flags);

> +

> +	if (!test_bit(ring->ring_id, ringacc->rings_inuse))

> +		return -EINVAL;

> +

> +	mutex_lock(&ringacc->req_lock);

> +

> +	if (--ring->use_count)

> +		goto out;

> +

> +	if (!(ring->flags & K3_RING_FLAG_BUSY))

> +		goto no_init;

> +

> +	k3_ringacc_ring_free_sci(ring);

> +

> +	dma_free_coherent(ringacc->dev,

> +			  ring->size * (4 << ring->elm_size),

> +			  ring->ring_mem_virt, ring->ring_mem_dma);

> +	ring->flags = 0;

> +	ring->ops = NULL;

> +	if (ring->proxy_id != K3_RINGACC_PROXY_NOT_USED) {

> +		clear_bit(ring->proxy_id, ringacc->proxy_inuse);

> +		ring->proxy = NULL;

> +		ring->proxy_id = K3_RINGACC_PROXY_NOT_USED;

> +	}

> +

> +no_init:

> +	clear_bit(ring->ring_id, ringacc->rings_inuse);

> +

> +out:

> +	mutex_unlock(&ringacc->req_lock);

> +	return 0;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_free);

> +

> +u32 k3_ringacc_get_ring_id(struct k3_ring *ring)

> +{

> +	if (!ring)

> +		return -EINVAL;

> +

> +	return ring->ring_id;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_get_ring_id);

> +

> +u32 k3_ringacc_get_tisci_dev_id(struct k3_ring *ring)

> +{

> +	if (!ring)

> +		return -EINVAL;

> +

> +	return ring->parent->tisci_dev_id;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_get_tisci_dev_id);

> +

> +int k3_ringacc_get_ring_irq_num(struct k3_ring *ring)

> +{

> +	int irq_num;

> +

> +	if (!ring)

> +		return -EINVAL;

> +

> +	irq_num = ti_sci_inta_msi_get_virq(ring->parent->dev, ring->ring_id);

> +	if (irq_num <= 0)

> +		irq_num = -EINVAL;

> +	return irq_num;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_get_ring_irq_num);

> +

> +static int k3_ringacc_ring_cfg_sci(struct k3_ring *ring)

> +{

> +	struct k3_ringacc *ringacc = ring->parent;

> +	u32 ring_idx;

> +	int ret;

> +

> +	if (!ringacc->tisci)

> +		return -EINVAL;

> +

> +	ring_idx = ring->ring_id;

> +	ret = ringacc->tisci_ring_ops->config(

> +			ringacc->tisci,

> +			TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER,

> +			ringacc->tisci_dev_id,

> +			ring_idx,

> +			lower_32_bits(ring->ring_mem_dma),

> +			upper_32_bits(ring->ring_mem_dma),

> +			ring->size,

> +			ring->mode,

> +			ring->elm_size,

> +			0);

> +	if (ret)

> +		dev_err(ringacc->dev, "TISCI config ring fail (%d) ring_idx %d\n",

> +			ret, ring_idx);

> +

> +	return ret;

> +}

> +

> +int k3_ringacc_ring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg)

> +{

> +	struct k3_ringacc *ringacc = ring->parent;

> +	int ret = 0;

> +

> +	if (!ring || !cfg)

> +		return -EINVAL;

> +	if (cfg->elm_size > K3_RINGACC_RING_ELSIZE_256 ||

> +	    cfg->mode >= K3_RINGACC_RING_MODE_INVALID ||

> +	    cfg->size & ~K3_RINGACC_CFG_RING_SIZE_ELCNT_MASK ||

> +	    !test_bit(ring->ring_id, ringacc->rings_inuse))

> +		return -EINVAL;

> +

> +	if (cfg->mode == K3_RINGACC_RING_MODE_MESSAGE &&

> +	    ring->proxy_id == K3_RINGACC_PROXY_NOT_USED &&

> +	    cfg->elm_size > K3_RINGACC_RING_ELSIZE_8) {

> +		dev_err(ringacc->dev,

> +			"Message mode must use proxy for %u element size\n",

> +			4 << ring->elm_size);

> +		return -EINVAL;

> +	}

> +

> +	/*

> +	 * In case of shared ring only the first user (master user) can

> +	 * configure the ring. The sequence should be by the client:

> +	 * ring = k3_ringacc_request_ring(ringacc, ring_id, 0); # master user

> +	 * k3_ringacc_ring_cfg(ring, cfg); # master configuration

> +	 * k3_ringacc_request_ring(ringacc, ring_id, K3_RING_FLAG_SHARED);

> +	 * k3_ringacc_request_ring(ringacc, ring_id, K3_RING_FLAG_SHARED);

> +	 */

> +	if (ring->use_count != 1)

> +		return 0;

> +

> +	ring->size = cfg->size;

> +	ring->elm_size = cfg->elm_size;

> +	ring->mode = cfg->mode;

> +	ring->occ = 0;

> +	ring->free = 0;

> +	ring->rindex = 0;

> +	ring->windex = 0;

> +

> +	if (ring->proxy_id != K3_RINGACC_PROXY_NOT_USED)

> +		ring->proxy = ringacc->proxy_target_base +

> +			      ring->proxy_id * K3_RINGACC_PROXY_TARGET_STEP;

> +

> +	switch (ring->mode) {

> +	case K3_RINGACC_RING_MODE_RING:

> +		ring->ops = &k3_ring_mode_ring_ops;

> +		break;

> +	case K3_RINGACC_RING_MODE_MESSAGE:

> +		if (ring->proxy)

> +			ring->ops = &k3_ring_mode_proxy_ops;

> +		else

> +			ring->ops = &k3_ring_mode_msg_ops;

> +		break;

> +	default:

> +		ring->ops = NULL;

> +		ret = -EINVAL;

> +		goto err_free_proxy;

> +	};

> +

> +	ring->ring_mem_virt = dma_alloc_coherent(ringacc->dev,

> +					ring->size * (4 << ring->elm_size),

> +					&ring->ring_mem_dma, GFP_KERNEL);

> +	if (!ring->ring_mem_virt) {

> +		dev_err(ringacc->dev, "Failed to alloc ring mem\n");

> +		ret = -ENOMEM;

> +		goto err_free_ops;

> +	}

> +

> +	ret = k3_ringacc_ring_cfg_sci(ring);

> +

> +	if (ret)

> +		goto err_free_mem;

> +

> +	ring->flags |= K3_RING_FLAG_BUSY;

> +	ring->flags |= (cfg->flags & K3_RINGACC_RING_SHARED) ?

> +			K3_RING_FLAG_SHARED : 0;

> +

> +	k3_ringacc_ring_dump(ring);

> +

> +	return 0;

> +

> +err_free_mem:

> +	dma_free_coherent(ringacc->dev,

> +			  ring->size * (4 << ring->elm_size),

> +			  ring->ring_mem_virt,

> +			  ring->ring_mem_dma);

> +err_free_ops:

> +	ring->ops = NULL;

> +err_free_proxy:

> +	ring->proxy = NULL;

> +	return ret;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_cfg);

> +

> +u32 k3_ringacc_ring_get_size(struct k3_ring *ring)

> +{

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return -EINVAL;

> +

> +	return ring->size;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_get_size);

> +

> +u32 k3_ringacc_ring_get_free(struct k3_ring *ring)

> +{

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return -EINVAL;

> +

> +	if (!ring->free)

> +		ring->free = ring->size - readl(&ring->rt->occ);

> +

> +	return ring->free;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_get_free);

> +

> +u32 k3_ringacc_ring_get_occ(struct k3_ring *ring)

> +{

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return -EINVAL;

> +

> +	return readl(&ring->rt->occ);

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_get_occ);

> +

> +u32 k3_ringacc_ring_is_full(struct k3_ring *ring)

> +{

> +	return !k3_ringacc_ring_get_free(ring);

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_is_full);

> +

> +enum k3_ringacc_access_mode {

> +	K3_RINGACC_ACCESS_MODE_PUSH_HEAD,

> +	K3_RINGACC_ACCESS_MODE_POP_HEAD,

> +	K3_RINGACC_ACCESS_MODE_PUSH_TAIL,

> +	K3_RINGACC_ACCESS_MODE_POP_TAIL,

> +	K3_RINGACC_ACCESS_MODE_PEEK_HEAD,

> +	K3_RINGACC_ACCESS_MODE_PEEK_TAIL,

> +};

> +

> +#define K3_RINGACC_PROXY_MODE(x)	(((x) & 0x3) << 16)

> +#define K3_RINGACC_PROXY_ELSIZE(x)	(((x) & 0x7) << 24)

> +static int k3_ringacc_ring_cfg_proxy(struct k3_ring *ring,

> +				     enum k3_ringacc_proxy_access_mode mode)

> +{

> +	u32 val;

> +

> +	val = ring->ring_id;

> +	val |= K3_RINGACC_PROXY_MODE(mode);

> +	val |= K3_RINGACC_PROXY_ELSIZE(ring->elm_size);

> +	writel(val, &ring->proxy->control);

> +	return 0;

> +}

> +

> +static int k3_ringacc_ring_access_proxy(struct k3_ring *ring, void *elem,

> +					enum k3_ringacc_access_mode access_mode)

> +{

> +	void __iomem *ptr;

> +

> +	ptr = (void __iomem *)&ring->proxy->data;

> +

> +	switch (access_mode) {

> +	case K3_RINGACC_ACCESS_MODE_PUSH_HEAD:

> +	case K3_RINGACC_ACCESS_MODE_POP_HEAD:

> +		k3_ringacc_ring_cfg_proxy(ring, PROXY_ACCESS_MODE_HEAD);

> +		break;

> +	case K3_RINGACC_ACCESS_MODE_PUSH_TAIL:

> +	case K3_RINGACC_ACCESS_MODE_POP_TAIL:

> +		k3_ringacc_ring_cfg_proxy(ring, PROXY_ACCESS_MODE_TAIL);

> +		break;

> +	default:

> +		return -EINVAL;

> +	}

> +

> +	ptr += k3_ringacc_ring_get_fifo_pos(ring);

> +

> +	switch (access_mode) {

> +	case K3_RINGACC_ACCESS_MODE_POP_HEAD:

> +	case K3_RINGACC_ACCESS_MODE_POP_TAIL:

> +		dev_dbg(ring->parent->dev,

> +			"proxy:memcpy_fromio(x): --> ptr(%p), mode:%d\n", ptr,

> +			access_mode);

> +		memcpy_fromio(elem, ptr, (4 << ring->elm_size));

> +		ring->occ--;

> +		break;

> +	case K3_RINGACC_ACCESS_MODE_PUSH_TAIL:

> +	case K3_RINGACC_ACCESS_MODE_PUSH_HEAD:

> +		dev_dbg(ring->parent->dev,

> +			"proxy:memcpy_toio(x): --> ptr(%p), mode:%d\n", ptr,

> +			access_mode);

> +		memcpy_toio(ptr, elem, (4 << ring->elm_size));

> +		ring->free--;

> +		break;

> +	default:

> +		return -EINVAL;

> +	}

> +

> +	dev_dbg(ring->parent->dev, "proxy: free%d occ%d\n", ring->free,

> +		ring->occ);

> +	return 0;

> +}

> +

> +static int k3_ringacc_ring_push_head_proxy(struct k3_ring *ring, void *elem)

> +{

> +	return k3_ringacc_ring_access_proxy(ring, elem,

> +					    K3_RINGACC_ACCESS_MODE_PUSH_HEAD);

> +}

> +

> +static int k3_ringacc_ring_push_tail_proxy(struct k3_ring *ring, void *elem)

> +{

> +	return k3_ringacc_ring_access_proxy(ring, elem,

> +					    K3_RINGACC_ACCESS_MODE_PUSH_TAIL);

> +}

> +

> +static int k3_ringacc_ring_pop_head_proxy(struct k3_ring *ring, void *elem)

> +{

> +	return k3_ringacc_ring_access_proxy(ring, elem,

> +					    K3_RINGACC_ACCESS_MODE_POP_HEAD);

> +}

> +

> +static int k3_ringacc_ring_pop_tail_proxy(struct k3_ring *ring, void *elem)

> +{

> +	return k3_ringacc_ring_access_proxy(ring, elem,

> +					    K3_RINGACC_ACCESS_MODE_POP_HEAD);

> +}

> +

> +static int k3_ringacc_ring_access_io(struct k3_ring *ring, void *elem,

> +				     enum k3_ringacc_access_mode access_mode)

> +{

> +	void __iomem *ptr;

> +

> +	switch (access_mode) {

> +	case K3_RINGACC_ACCESS_MODE_PUSH_HEAD:

> +	case K3_RINGACC_ACCESS_MODE_POP_HEAD:

> +		ptr = (void __iomem *)&ring->fifos->head_data;

> +		break;

> +	case K3_RINGACC_ACCESS_MODE_PUSH_TAIL:

> +	case K3_RINGACC_ACCESS_MODE_POP_TAIL:

> +		ptr = (void __iomem *)&ring->fifos->tail_data;

> +		break;

> +	default:

> +		return -EINVAL;

> +	}

> +

> +	ptr += k3_ringacc_ring_get_fifo_pos(ring);

> +

> +	switch (access_mode) {

> +	case K3_RINGACC_ACCESS_MODE_POP_HEAD:

> +	case K3_RINGACC_ACCESS_MODE_POP_TAIL:

> +		dev_dbg(ring->parent->dev,

> +			"memcpy_fromio(x): --> ptr(%p), mode:%d\n", ptr,

> +			access_mode);

> +		memcpy_fromio(elem, ptr, (4 << ring->elm_size));

> +		ring->occ--;

> +		break;

> +	case K3_RINGACC_ACCESS_MODE_PUSH_TAIL:

> +	case K3_RINGACC_ACCESS_MODE_PUSH_HEAD:

> +		dev_dbg(ring->parent->dev,

> +			"memcpy_toio(x): --> ptr(%p), mode:%d\n", ptr,

> +			access_mode);

> +		memcpy_toio(ptr, elem, (4 << ring->elm_size));

> +		ring->free--;

> +		break;

> +	default:

> +		return -EINVAL;

> +	}

> +

> +	dev_dbg(ring->parent->dev, "free%d index%d occ%d index%d\n", ring->free,

> +		ring->windex, ring->occ, ring->rindex);

> +	return 0;

> +}

> +

> +static int k3_ringacc_ring_push_head_io(struct k3_ring *ring, void *elem)

> +{

> +	return k3_ringacc_ring_access_io(ring, elem,

> +					 K3_RINGACC_ACCESS_MODE_PUSH_HEAD);

> +}

> +

> +static int k3_ringacc_ring_push_io(struct k3_ring *ring, void *elem)

> +{

> +	return k3_ringacc_ring_access_io(ring, elem,

> +					 K3_RINGACC_ACCESS_MODE_PUSH_TAIL);

> +}

> +

> +static int k3_ringacc_ring_pop_io(struct k3_ring *ring, void *elem)

> +{

> +	return k3_ringacc_ring_access_io(ring, elem,

> +					 K3_RINGACC_ACCESS_MODE_POP_HEAD);

> +}

> +

> +static int k3_ringacc_ring_pop_tail_io(struct k3_ring *ring, void *elem)

> +{

> +	return k3_ringacc_ring_access_io(ring, elem,

> +					 K3_RINGACC_ACCESS_MODE_POP_HEAD);

> +}

> +

> +static int k3_ringacc_ring_push_mem(struct k3_ring *ring, void *elem)

> +{

> +	void *elem_ptr;

> +

> +	elem_ptr = k3_ringacc_get_elm_addr(ring, ring->windex);

> +

> +	memcpy(elem_ptr, elem, (4 << ring->elm_size));

> +

> +	ring->windex = (ring->windex + 1) % ring->size;

> +	ring->free--;

> +	writel(1, &ring->rt->db);

> +

> +	dev_dbg(ring->parent->dev, "ring_push_mem: free%d index%d\n",

> +		ring->free, ring->windex);

> +

> +	return 0;

> +}

> +

> +static int k3_ringacc_ring_pop_mem(struct k3_ring *ring, void *elem)

> +{

> +	void *elem_ptr;

> +

> +	elem_ptr = k3_ringacc_get_elm_addr(ring, ring->rindex);

> +

> +	memcpy(elem, elem_ptr, (4 << ring->elm_size));

> +

> +	ring->rindex = (ring->rindex + 1) % ring->size;

> +	ring->occ--;

> +	writel(-1, &ring->rt->db);

> +

> +	dev_dbg(ring->parent->dev, "ring_pop_mem: occ%d index%d pos_ptr%p\n",

> +		ring->occ, ring->rindex, elem_ptr);

> +	return 0;

> +}

> +

> +int k3_ringacc_ring_push(struct k3_ring *ring, void *elem)

> +{

> +	int ret = -EOPNOTSUPP;

> +

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return -EINVAL;

> +

> +	dev_dbg(ring->parent->dev, "ring_push: free%d index%d\n", ring->free,

> +		ring->windex);

> +

> +	if (k3_ringacc_ring_is_full(ring))

> +		return -ENOMEM;

> +

> +	if (ring->ops && ring->ops->push_tail)

> +		ret = ring->ops->push_tail(ring, elem);

> +

> +	return ret;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_push);

> +

> +int k3_ringacc_ring_push_head(struct k3_ring *ring, void *elem)

> +{

> +	int ret = -EOPNOTSUPP;

> +

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return -EINVAL;

> +

> +	dev_dbg(ring->parent->dev, "ring_push_head: free%d index%d\n",

> +		ring->free, ring->windex);

> +

> +	if (k3_ringacc_ring_is_full(ring))

> +		return -ENOMEM;

> +

> +	if (ring->ops && ring->ops->push_head)

> +		ret = ring->ops->push_head(ring, elem);

> +

> +	return ret;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_push_head);

> +

> +int k3_ringacc_ring_pop(struct k3_ring *ring, void *elem)

> +{

> +	int ret = -EOPNOTSUPP;

> +

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return -EINVAL;

> +

> +	if (!ring->occ)

> +		ring->occ = k3_ringacc_ring_get_occ(ring);

> +

> +	dev_dbg(ring->parent->dev, "ring_pop: occ%d index%d\n", ring->occ,

> +		ring->rindex);

> +

> +	if (!ring->occ)

> +		return -ENODATA;

> +

> +	if (ring->ops && ring->ops->pop_head)

> +		ret = ring->ops->pop_head(ring, elem);

> +

> +	return ret;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_pop);

> +

> +int k3_ringacc_ring_pop_tail(struct k3_ring *ring, void *elem)

> +{

> +	int ret = -EOPNOTSUPP;

> +

> +	if (!ring || !(ring->flags & K3_RING_FLAG_BUSY))

> +		return -EINVAL;

> +

> +	if (!ring->occ)

> +		ring->occ = k3_ringacc_ring_get_occ(ring);

> +

> +	dev_dbg(ring->parent->dev, "ring_pop_tail: occ%d index%d\n", ring->occ,

> +		ring->rindex);

> +

> +	if (!ring->occ)

> +		return -ENODATA;

> +

> +	if (ring->ops && ring->ops->pop_tail)

> +		ret = ring->ops->pop_tail(ring, elem);

> +

> +	return ret;

> +}

> +EXPORT_SYMBOL_GPL(k3_ringacc_ring_pop_tail);

> +

> +struct k3_ringacc *of_k3_ringacc_get_by_phandle(struct device_node *np,

> +						const char *property)

> +{

> +	struct device_node *ringacc_np;

> +	struct k3_ringacc *ringacc = ERR_PTR(-EPROBE_DEFER);

> +	struct k3_ringacc *entry;

> +

> +	ringacc_np = of_parse_phandle(np, property, 0);

> +	if (!ringacc_np)

> +		return ERR_PTR(-ENODEV);

> +

> +	mutex_lock(&k3_ringacc_list_lock);

> +	list_for_each_entry(entry, &k3_ringacc_list, list)

> +		if (entry->dev->of_node == ringacc_np) {

> +			ringacc = entry;

> +			break;

> +		}

> +	mutex_unlock(&k3_ringacc_list_lock);

> +	of_node_put(ringacc_np);

> +

> +	return ringacc;

> +}

> +EXPORT_SYMBOL_GPL(of_k3_ringacc_get_by_phandle);

> +

> +static int k3_ringacc_probe_dt(struct k3_ringacc *ringacc)

> +{

> +	struct device_node *node = ringacc->dev->of_node;

> +	struct device *dev = ringacc->dev;

> +	struct platform_device *pdev = to_platform_device(dev);

> +	int ret;

> +

> +	if (!node) {

> +		dev_err(dev, "device tree info unavailable\n");

> +		return -ENODEV;

> +	}

> +

> +	ret = of_property_read_u32(node, "ti,num-rings", &ringacc->num_rings);

> +	if (ret) {

> +		dev_err(dev, "ti,num-rings read failure %d\n", ret);

> +		return ret;

> +	}

> +

> +	ringacc->dma_ring_reset_quirk =

> +			of_property_read_bool(node, "ti,dma-ring-reset-quirk");

> +

> +	ringacc->tisci = ti_sci_get_by_phandle(node, "ti,sci");

> +	if (IS_ERR(ringacc->tisci)) {

> +		ret = PTR_ERR(ringacc->tisci);

> +		if (ret != -EPROBE_DEFER)

> +			dev_err(dev, "ti,sci read fail %d\n", ret);

> +		ringacc->tisci = NULL;

> +		return ret;

> +	}

> +

> +	ret = of_property_read_u32(node, "ti,sci-dev-id",

> +				   &ringacc->tisci_dev_id);

> +	if (ret) {

> +		dev_err(dev, "ti,sci-dev-id read fail %d\n", ret);

> +		return ret;

> +	}

> +

> +	pdev->id = ringacc->tisci_dev_id;

> +

> +	ringacc->rm_gp_range = devm_ti_sci_get_of_resource(ringacc->tisci, dev,

> +						ringacc->tisci_dev_id,

> +						"ti,sci-rm-range-gp-rings");

> +	if (IS_ERR(ringacc->rm_gp_range)) {

> +		dev_err(dev, "Failed to allocate MSI interrupts\n");

> +		return PTR_ERR(ringacc->rm_gp_range);

> +	}

> +

> +	return ti_sci_inta_msi_domain_alloc_irqs(ringacc->dev,

> +						 ringacc->rm_gp_range);

> +}

> +

> +static int k3_ringacc_probe(struct platform_device *pdev)

> +{

> +	struct k3_ringacc *ringacc;

> +	void __iomem *base_fifo, *base_rt;

> +	struct device *dev = &pdev->dev;

> +	struct resource *res;

> +	int ret, i;

> +

> +	ringacc = devm_kzalloc(dev, sizeof(*ringacc), GFP_KERNEL);

> +	if (!ringacc)

> +		return -ENOMEM;

> +

> +	ringacc->dev = dev;

> +	mutex_init(&ringacc->req_lock);

> +

> +	dev->msi_domain = of_msi_get_domain(dev, dev->of_node,

> +					    DOMAIN_BUS_TI_SCI_INTA_MSI);

> +	if (!dev->msi_domain) {

> +		dev_err(dev, "Failed to get MSI domain\n");

> +		return -EPROBE_DEFER;

> +	}

> +

> +	ret = k3_ringacc_probe_dt(ringacc);

> +	if (ret)

> +		return ret;

> +

> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rt");

> +	base_rt = devm_ioremap_resource(dev, res);

> +	if (IS_ERR(base_rt))

> +		return PTR_ERR(base_rt);

> +

> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fifos");

> +	base_fifo = devm_ioremap_resource(dev, res);

> +	if (IS_ERR(base_fifo))

> +		return PTR_ERR(base_fifo);

> +

> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "proxy_gcfg");

> +	ringacc->proxy_gcfg = devm_ioremap_resource(dev, res);

> +	if (IS_ERR(ringacc->proxy_gcfg))

> +		return PTR_ERR(ringacc->proxy_gcfg);

> +

> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,

> +					   "proxy_target");

> +	ringacc->proxy_target_base = devm_ioremap_resource(dev, res);

> +	if (IS_ERR(ringacc->proxy_target_base))

> +		return PTR_ERR(ringacc->proxy_target_base);

> +

> +	ringacc->num_proxies = readl(&ringacc->proxy_gcfg->config) &

> +				     K3_RINGACC_PROXY_CFG_THREADS_MASK;

> +

> +	ringacc->rings = devm_kzalloc(dev,

> +				      sizeof(*ringacc->rings) *

> +				      ringacc->num_rings,

> +				      GFP_KERNEL);

> +	ringacc->rings_inuse = devm_kcalloc(dev,

> +					    BITS_TO_LONGS(ringacc->num_rings),

> +					    sizeof(unsigned long), GFP_KERNEL);

> +	ringacc->proxy_inuse = devm_kcalloc(dev,

> +					    BITS_TO_LONGS(ringacc->num_proxies),

> +					    sizeof(unsigned long), GFP_KERNEL);

> +

> +	if (!ringacc->rings || !ringacc->rings_inuse || !ringacc->proxy_inuse)

> +		return -ENOMEM;

> +

> +	for (i = 0; i < ringacc->num_rings; i++) {

> +		ringacc->rings[i].rt = base_rt +

> +				       K3_RINGACC_RT_REGS_STEP * i;

> +		ringacc->rings[i].fifos = base_fifo +

> +					  K3_RINGACC_FIFO_REGS_STEP * i;

> +		ringacc->rings[i].parent = ringacc;

> +		ringacc->rings[i].ring_id = i;

> +		ringacc->rings[i].proxy_id = K3_RINGACC_PROXY_NOT_USED;

> +	}

> +	dev_set_drvdata(dev, ringacc);

> +

> +	ringacc->tisci_ring_ops = &ringacc->tisci->ops.rm_ring_ops;

> +

> +	mutex_lock(&k3_ringacc_list_lock);

> +	list_add_tail(&ringacc->list, &k3_ringacc_list);

> +	mutex_unlock(&k3_ringacc_list_lock);

> +

> +	dev_info(dev, "Ring Accelerator probed rings:%u, gp-rings[%u,%u] sci-dev-id:%u\n",

> +		 ringacc->num_rings,

> +		 ringacc->rm_gp_range->desc[0].start,

> +		 ringacc->rm_gp_range->desc[0].num,

> +		 ringacc->tisci_dev_id);

> +	dev_info(dev, "dma-ring-reset-quirk: %s\n",

> +		 ringacc->dma_ring_reset_quirk ? "enabled" : "disabled");

> +	dev_info(dev, "RA Proxy rev. %08x, num_proxies:%u\n",

> +		 readl(&ringacc->proxy_gcfg->revision), ringacc->num_proxies);

> +	return 0;

> +}

> +

> +/* Match table for of_platform binding */

> +static const struct of_device_id k3_ringacc_of_match[] = {

> +	{ .compatible = "ti,am654-navss-ringacc", },

> +	{},

> +};

> +

> +static struct platform_driver k3_ringacc_driver = {

> +	.probe		= k3_ringacc_probe,

> +	.driver		= {

> +		.name	= "k3-ringacc",

> +		.of_match_table = k3_ringacc_of_match,

> +		.suppress_bind_attrs = true,

> +	},

> +};

> +builtin_platform_driver(k3_ringacc_driver);

> diff --git a/include/linux/soc/ti/k3-ringacc.h b/include/linux/soc/ti/k3-ringacc.h

> new file mode 100644

> index 000000000000..26f73df0a524

> --- /dev/null

> +++ b/include/linux/soc/ti/k3-ringacc.h

> @@ -0,0 +1,244 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * K3 Ring Accelerator (RA) subsystem interface

> + *

> + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com

> + */

> +

> +#ifndef __SOC_TI_K3_RINGACC_API_H_

> +#define __SOC_TI_K3_RINGACC_API_H_

> +

> +#include <linux/types.h>

> +

> +struct device_node;

> +

> +/**

> + * enum k3_ring_mode - &struct k3_ring_cfg mode

> + *

> + * RA ring operational modes

> + *

> + * @K3_RINGACC_RING_MODE_RING: Exposed Ring mode for SW direct access

> + * @K3_RINGACC_RING_MODE_MESSAGE: Messaging mode. Messaging mode requires

> + *	that all accesses to the queue must go through this IP so that all

> + *	accesses to the memory are controlled and ordered. This IP then

> + *	controls the entire state of the queue, and SW has no directly control,

> + *	such as through doorbells and cannot access the storage memory directly.

> + *	This is particularly useful when more than one SW or HW entity can be

> + *	the producer and/or consumer at the same time

> + * @K3_RINGACC_RING_MODE_CREDENTIALS: Credentials mode is message mode plus

> + *	stores credentials with each message, requiring the element size to be

> + *	doubled to fit the credentials. Any exposed memory should be protected

> + *	by a firewall from unwanted access

> + */

> +enum k3_ring_mode {

> +	K3_RINGACC_RING_MODE_RING = 0,

> +	K3_RINGACC_RING_MODE_MESSAGE,

> +	K3_RINGACC_RING_MODE_CREDENTIALS,

> +	K3_RINGACC_RING_MODE_INVALID

> +};

> +

> +/**

> + * enum k3_ring_size - &struct k3_ring_cfg elm_size

> + *

> + * RA ring element's sizes in bytes.

> + */

> +enum k3_ring_size {

> +	K3_RINGACC_RING_ELSIZE_4 = 0,

> +	K3_RINGACC_RING_ELSIZE_8,

> +	K3_RINGACC_RING_ELSIZE_16,

> +	K3_RINGACC_RING_ELSIZE_32,

> +	K3_RINGACC_RING_ELSIZE_64,

> +	K3_RINGACC_RING_ELSIZE_128,

> +	K3_RINGACC_RING_ELSIZE_256,

> +	K3_RINGACC_RING_ELSIZE_INVALID

> +};

> +

> +struct k3_ringacc;

> +struct k3_ring;

> +

> +/**

> + * enum k3_ring_cfg - RA ring configuration structure

> + *

> + * @size: Ring size, number of elements

> + * @elm_size: Ring element size

> + * @mode: Ring operational mode

> + * @flags: Ring configuration flags. Possible values:

> + *	 @K3_RINGACC_RING_SHARED: when set allows to request the same ring

> + *	 few times. It's usable when the same ring is used as Free Host PD ring

> + *	 for different flows, for example.

> + *	 Note: Locking should be done by consumer if required

> + */

> +struct k3_ring_cfg {

> +	u32 size;

> +	enum k3_ring_size elm_size;

> +	enum k3_ring_mode mode;

> +#define K3_RINGACC_RING_SHARED BIT(1)

> +	u32 flags;

> +};

> +

> +#define K3_RINGACC_RING_ID_ANY (-1)

> +

> +/**

> + * of_k3_ringacc_get_by_phandle - find a RA by phandle property

> + * @np: device node

> + * @propname: property name containing phandle on RA node

> + *

> + * Returns pointer on the RA - struct k3_ringacc

> + * or -ENODEV if not found,

> + * or -EPROBE_DEFER if not yet registered

> + */

> +struct k3_ringacc *of_k3_ringacc_get_by_phandle(struct device_node *np,

> +						const char *property);

> +

> +#define K3_RINGACC_RING_USE_PROXY BIT(1)

> +

> +/**

> + * k3_ringacc_request_ring - request ring from ringacc

> + * @ringacc: pointer on ringacc

> + * @id: ring id or K3_RINGACC_RING_ID_ANY for any general purpose ring

> + * @flags:

> + *	@K3_RINGACC_RING_USE_PROXY: if set - proxy will be allocated and

> + *		used to access ring memory. Sopported only for rings in

> + *		Message/Credentials/Queue mode.

> + *

> + * Returns pointer on the Ring - struct k3_ring

> + * or NULL in case of failure.

> + */

> +struct k3_ring *k3_ringacc_request_ring(struct k3_ringacc *ringacc,

> +					int id, u32 flags);

> +

> +/**

> + * k3_ringacc_ring_reset - ring reset

> + * @ring: pointer on Ring

> + *

> + * Resets ring internal state ((hw)occ, (hw)idx).

> + */

> +void k3_ringacc_ring_reset(struct k3_ring *ring);

> +/**

> + * k3_ringacc_ring_reset - ring reset for DMA rings

> + * @ring: pointer on Ring

> + *

> + * Resets ring internal state ((hw)occ, (hw)idx). Should be used for rings

> + * which are read by K3 UDMA, like TX or Free Host PD rings.

> + */

> +void k3_ringacc_ring_reset_dma(struct k3_ring *ring, u32 occ);

> +

> +/**

> + * k3_ringacc_ring_free - ring free

> + * @ring: pointer on Ring

> + *

> + * Resets ring and free all alocated resources.

> + */

> +int k3_ringacc_ring_free(struct k3_ring *ring);

> +

> +/**

> + * k3_ringacc_get_ring_id - Get the Ring ID

> + * @ring: pointer on ring

> + *

> + * Returns the Ring ID

> + */

> +u32 k3_ringacc_get_ring_id(struct k3_ring *ring);

> +

> +/**

> + * k3_ringacc_get_ring_irq_num - Get the irq number for the ring

> + * @ring: pointer on ring

> + *

> + * Returns the interrupt number which can be used to request the interrupt

> + */

> +int k3_ringacc_get_ring_irq_num(struct k3_ring *ring);

> +

> +/**

> + * k3_ringacc_ring_cfg - ring configure

> + * @ring: pointer on ring

> + * @cfg: Ring configuration parameters (see &struct k3_ring_cfg)

> + *

> + * Configures ring, including ring memory allocation.

> + * Returns 0 on success, errno otherwise.

> + */

> +int k3_ringacc_ring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg);

> +

> +/**

> + * k3_ringacc_ring_get_size - get ring size

> + * @ring: pointer on ring

> + *

> + * Returns ring size in number of elements.

> + */

> +u32 k3_ringacc_ring_get_size(struct k3_ring *ring);

> +

> +/**

> + * k3_ringacc_ring_get_free - get free elements

> + * @ring: pointer on ring

> + *

> + * Returns number of free elements in the ring.

> + */

> +u32 k3_ringacc_ring_get_free(struct k3_ring *ring);

> +

> +/**

> + * k3_ringacc_ring_get_occ - get ring occupancy

> + * @ring: pointer on ring

> + *

> + * Returns total number of valid entries on the ring

> + */

> +u32 k3_ringacc_ring_get_occ(struct k3_ring *ring);

> +

> +/**

> + * k3_ringacc_ring_is_full - checks if ring is full

> + * @ring: pointer on ring

> + *

> + * Returns true if the ring is full

> + */

> +u32 k3_ringacc_ring_is_full(struct k3_ring *ring);

> +

> +/**

> + * k3_ringacc_ring_push - push element to the ring tail

> + * @ring: pointer on ring

> + * @elem: pointer on ring element buffer

> + *

> + * Push one ring element to the ring tail. Size of the ring element is

> + * determined by ring configuration &struct k3_ring_cfg elm_size.

> + *

> + * Returns 0 on success, errno otherwise.

> + */

> +int k3_ringacc_ring_push(struct k3_ring *ring, void *elem);

> +

> +/**

> + * k3_ringacc_ring_pop - pop element from the ring head

> + * @ring: pointer on ring

> + * @elem: pointer on ring element buffer

> + *

> + * Push one ring element from the ring head. Size of the ring element is

> + * determined by ring configuration &struct k3_ring_cfg elm_size..

> + *

> + * Returns 0 on success, errno otherwise.

> + */

> +int k3_ringacc_ring_pop(struct k3_ring *ring, void *elem);

> +

> +/**

> + * k3_ringacc_ring_push_head - push element to the ring head

> + * @ring: pointer on ring

> + * @elem: pointer on ring element buffer

> + *

> + * Push one ring element to the ring head. Size of the ring element is

> + * determined by ring configuration &struct k3_ring_cfg elm_size.

> + *

> + * Returns 0 on success, errno otherwise.

> + * Not Supported by ring modes: K3_RINGACC_RING_MODE_RING

> + */

> +int k3_ringacc_ring_push_head(struct k3_ring *ring, void *elem);

> +

> +/**

> + * k3_ringacc_ring_pop_tail - pop element from the ring tail

> + * @ring: pointer on ring

> + * @elem: pointer on ring element buffer

> + *

> + * Push one ring element from the ring tail. Size of the ring element is

> + * determined by ring configuration &struct k3_ring_cfg elm_size.

> + *

> + * Returns 0 on success, errno otherwise.

> + * Not Supported by ring modes: K3_RINGACC_RING_MODE_RING

> + */

> +int k3_ringacc_ring_pop_tail(struct k3_ring *ring, void *elem);

> +

> +u32 k3_ringacc_get_tisci_dev_id(struct k3_ring *ring);

> +

> +#endif /* __SOC_TI_K3_RINGACC_API_H_ */

> 


Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki