dmaengine: ti: k3-udma: Add glue layer for non DMAengine users

Certain users can not use right now the DMAengine API due to missing
features in the core. Prime example is Networking.

These users can use the glue layer interface to avoid misuse of DMAengine
API and when the core gains the needed features they can be converted to
use generic API.

The most prominent features the glue layer clients are depending on:

- most PSI-L native peripheral use extra rflow ranges on a receive channel
   and depending on the peripheral's configuration packets from a single
   free descriptor ring is going to be received to different receive ring
  - it is also possible to have different free descriptor rings per rflow
    and an rflow can also support 4 additional free descriptor ring based
    on the size of the incoming packet
- 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'
- the notion of prep_slave_sg is not matching with what the streaming type
   of operation is demanding for networking
- Streaming type of operation
 - Ability to fill the free descriptor ring with descriptors in
   anticipation of incoming traffic and when a packet arrives UDMAP will
   form a packet and gives it to the client driver
 - the descriptors are not backed with exact size data buffers as we don't
   know the size of the packet we will receive, but as a generic pool of
   buffers to be used by the receive channel
- 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

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Tested-by: Keerthy <j-keerthy@ti.com>
Link: https://lore.kernel.org/r/20191223110458.30766-12-peter.ujfalusi@ti.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Grygorii Strashko 2019-12-23 13:04:51 +02:00 committed by Vinod Koul
parent 25dcb5dd7b
commit d702419134
7 changed files with 1567 additions and 1 deletions

View File

@ -47,6 +47,14 @@ config TI_K3_UDMA
Enable support for the TI UDMA (Unified DMA) controller. This
DMA engine is used in AM65x and j721e.
config TI_K3_UDMA_GLUE_LAYER
bool "Texas Instruments UDMA Glue layer for non DMAengine users"
depends on ARCH_K3 || COMPILE_TEST
depends on TI_K3_UDMA
help
Say y here to support the K3 NAVSS DMA glue interface
If unsure, say N.
config TI_K3_PSIL
bool

View File

@ -3,5 +3,6 @@ obj-$(CONFIG_TI_CPPI41) += cppi41.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
obj-$(CONFIG_TI_K3_UDMA) += k3-udma.o
obj-$(CONFIG_TI_K3_UDMA_GLUE_LAYER) += k3-udma-glue.o
obj-$(CONFIG_TI_K3_PSIL) += k3-psil.o k3-psil-am654.o k3-psil-j721e.o
obj-$(CONFIG_TI_DMA_CROSSBAR) += dma-crossbar.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*/
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
{
return navss_psil_pair(ud, src_thread, dst_thread);
}
EXPORT_SYMBOL(xudma_navss_psil_pair);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
{
return navss_psil_unpair(ud, src_thread, dst_thread);
}
EXPORT_SYMBOL(xudma_navss_psil_unpair);
struct udma_dev *of_xudma_dev_get(struct device_node *np, const char *property)
{
struct device_node *udma_node = np;
struct platform_device *pdev;
struct udma_dev *ud;
if (property) {
udma_node = of_parse_phandle(np, property, 0);
if (!udma_node) {
pr_err("UDMA node is not found\n");
return ERR_PTR(-ENODEV);
}
}
pdev = of_find_device_by_node(udma_node);
if (!pdev) {
pr_debug("UDMA device not found\n");
return ERR_PTR(-EPROBE_DEFER);
}
if (np != udma_node)
of_node_put(udma_node);
ud = platform_get_drvdata(pdev);
if (!ud) {
pr_debug("UDMA has not been probed\n");
return ERR_PTR(-EPROBE_DEFER);
}
return ud;
}
EXPORT_SYMBOL(of_xudma_dev_get);
u32 xudma_dev_get_psil_base(struct udma_dev *ud)
{
return ud->psil_base;
}
EXPORT_SYMBOL(xudma_dev_get_psil_base);
struct udma_tisci_rm *xudma_dev_get_tisci_rm(struct udma_dev *ud)
{
return &ud->tisci_rm;
}
EXPORT_SYMBOL(xudma_dev_get_tisci_rm);
int xudma_alloc_gp_rflow_range(struct udma_dev *ud, int from, int cnt)
{
return __udma_alloc_gp_rflow_range(ud, from, cnt);
}
EXPORT_SYMBOL(xudma_alloc_gp_rflow_range);
int xudma_free_gp_rflow_range(struct udma_dev *ud, int from, int cnt)
{
return __udma_free_gp_rflow_range(ud, from, cnt);
}
EXPORT_SYMBOL(xudma_free_gp_rflow_range);
bool xudma_rflow_is_gp(struct udma_dev *ud, int id)
{
return !test_bit(id, ud->rflow_gp_map);
}
EXPORT_SYMBOL(xudma_rflow_is_gp);
#define XUDMA_GET_PUT_RESOURCE(res) \
struct udma_##res *xudma_##res##_get(struct udma_dev *ud, int id) \
{ \
return __udma_reserve_##res(ud, false, id); \
} \
EXPORT_SYMBOL(xudma_##res##_get); \
\
void xudma_##res##_put(struct udma_dev *ud, struct udma_##res *p) \
{ \
clear_bit(p->id, ud->res##_map); \
} \
EXPORT_SYMBOL(xudma_##res##_put)
XUDMA_GET_PUT_RESOURCE(tchan);
XUDMA_GET_PUT_RESOURCE(rchan);
struct udma_rflow *xudma_rflow_get(struct udma_dev *ud, int id)
{
return __udma_get_rflow(ud, id);
}
EXPORT_SYMBOL(xudma_rflow_get);
void xudma_rflow_put(struct udma_dev *ud, struct udma_rflow *p)
{
__udma_put_rflow(ud, p);
}
EXPORT_SYMBOL(xudma_rflow_put);
#define XUDMA_GET_RESOURCE_ID(res) \
int xudma_##res##_get_id(struct udma_##res *p) \
{ \
return p->id; \
} \
EXPORT_SYMBOL(xudma_##res##_get_id)
XUDMA_GET_RESOURCE_ID(tchan);
XUDMA_GET_RESOURCE_ID(rchan);
XUDMA_GET_RESOURCE_ID(rflow);
/* Exported register access functions */
#define XUDMA_RT_IO_FUNCTIONS(res) \
u32 xudma_##res##rt_read(struct udma_##res *p, int reg) \
{ \
return udma_##res##rt_read(p, reg); \
} \
EXPORT_SYMBOL(xudma_##res##rt_read); \
\
void xudma_##res##rt_write(struct udma_##res *p, int reg, u32 val) \
{ \
udma_##res##rt_write(p, reg, val); \
} \
EXPORT_SYMBOL(xudma_##res##rt_write)
XUDMA_RT_IO_FUNCTIONS(tchan);
XUDMA_RT_IO_FUNCTIONS(rchan);

View File

@ -1090,6 +1090,64 @@ static irqreturn_t udma_udma_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
/**
* __udma_alloc_gp_rflow_range - alloc range of GP RX flows
* @ud: UDMA device
* @from: Start the search from this flow id number
* @cnt: Number of consecutive flow ids to allocate
*
* Allocate range of RX flow ids for future use, those flows can be requested
* only using explicit flow id number. if @from is set to -1 it will try to find
* first free range. if @from is positive value it will force allocation only
* of the specified range of flows.
*
* Returns -ENOMEM if can't find free range.
* -EEXIST if requested range is busy.
* -EINVAL if wrong input values passed.
* Returns flow id on success.
*/
static int __udma_alloc_gp_rflow_range(struct udma_dev *ud, int from, int cnt)
{
int start, tmp_from;
DECLARE_BITMAP(tmp, K3_UDMA_MAX_RFLOWS);
tmp_from = from;
if (tmp_from < 0)
tmp_from = ud->rchan_cnt;
/* default flows can't be allocated and accessible only by id */
if (tmp_from < ud->rchan_cnt)
return -EINVAL;
if (tmp_from + cnt > ud->rflow_cnt)
return -EINVAL;
bitmap_or(tmp, ud->rflow_gp_map, ud->rflow_gp_map_allocated,
ud->rflow_cnt);
start = bitmap_find_next_zero_area(tmp,
ud->rflow_cnt,
tmp_from, cnt, 0);
if (start >= ud->rflow_cnt)
return -ENOMEM;
if (from >= 0 && start != from)
return -EEXIST;
bitmap_set(ud->rflow_gp_map_allocated, start, cnt);
return start;
}
static int __udma_free_gp_rflow_range(struct udma_dev *ud, int from, int cnt)
{
if (from < ud->rchan_cnt)
return -EINVAL;
if (from + cnt > ud->rflow_cnt)
return -EINVAL;
bitmap_clear(ud->rflow_gp_map_allocated, from, cnt);
return 0;
}
static struct udma_rflow *__udma_get_rflow(struct udma_dev *ud, int id)
{
/*
@ -2968,7 +3026,7 @@ static struct udma_match_data am654_main_data = {
static struct udma_match_data am654_mcu_data = {
.psil_base = 0x6000,
.enable_memcpy_support = false, /* MEM_TO_MEM is slow via MCU UDMA */
.enable_memcpy_support = true, /* TEST: DMA domains */
.statictr_z_mask = GENMASK(11, 0),
.rchan_oes_offset = 0x2000,
.tpl_levels = 2,
@ -3369,3 +3427,6 @@ static struct platform_driver udma_driver = {
.probe = udma_probe,
};
builtin_platform_driver(udma_driver);
/* Private interfaces to UDMA */
#include "k3-udma-private.c"

View File

@ -117,4 +117,35 @@ struct udma_tisci_rm {
struct ti_sci_resource *rm_ranges[RM_RANGE_LAST];
};
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
u32 dst_thread);
struct udma_dev *of_xudma_dev_get(struct device_node *np, const char *property);
void xudma_dev_put(struct udma_dev *ud);
u32 xudma_dev_get_psil_base(struct udma_dev *ud);
struct udma_tisci_rm *xudma_dev_get_tisci_rm(struct udma_dev *ud);
int xudma_alloc_gp_rflow_range(struct udma_dev *ud, int from, int cnt);
int xudma_free_gp_rflow_range(struct udma_dev *ud, int from, int cnt);
struct udma_tchan *xudma_tchan_get(struct udma_dev *ud, int id);
struct udma_rchan *xudma_rchan_get(struct udma_dev *ud, int id);
struct udma_rflow *xudma_rflow_get(struct udma_dev *ud, int id);
void xudma_tchan_put(struct udma_dev *ud, struct udma_tchan *p);
void xudma_rchan_put(struct udma_dev *ud, struct udma_rchan *p);
void xudma_rflow_put(struct udma_dev *ud, struct udma_rflow *p);
int xudma_tchan_get_id(struct udma_tchan *p);
int xudma_rchan_get_id(struct udma_rchan *p);
int xudma_rflow_get_id(struct udma_rflow *p);
u32 xudma_tchanrt_read(struct udma_tchan *tchan, int reg);
void xudma_tchanrt_write(struct udma_tchan *tchan, int reg, u32 val);
u32 xudma_rchanrt_read(struct udma_rchan *rchan, int reg);
void xudma_rchanrt_write(struct udma_rchan *rchan, int reg, u32 val);
bool xudma_rflow_is_gp(struct udma_dev *ud, int id);
#endif /* K3_UDMA_H_ */

View File

@ -0,0 +1,134 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
*/
#ifndef K3_UDMA_GLUE_H_
#define K3_UDMA_GLUE_H_
#include <linux/types.h>
#include <linux/soc/ti/k3-ringacc.h>
#include <linux/dma/ti-cppi5.h>
struct k3_udma_glue_tx_channel_cfg {
struct k3_ring_cfg tx_cfg;
struct k3_ring_cfg txcq_cfg;
bool tx_pause_on_err;
bool tx_filt_einfo;
bool tx_filt_pswords;
bool tx_supr_tdpkt;
u32 swdata_size;
};
struct k3_udma_glue_tx_channel;
struct k3_udma_glue_tx_channel *k3_udma_glue_request_tx_chn(struct device *dev,
const char *name, struct k3_udma_glue_tx_channel_cfg *cfg);
void k3_udma_glue_release_tx_chn(struct k3_udma_glue_tx_channel *tx_chn);
int k3_udma_glue_push_tx_chn(struct k3_udma_glue_tx_channel *tx_chn,
struct cppi5_host_desc_t *desc_tx,
dma_addr_t desc_dma);
int k3_udma_glue_pop_tx_chn(struct k3_udma_glue_tx_channel *tx_chn,
dma_addr_t *desc_dma);
int k3_udma_glue_enable_tx_chn(struct k3_udma_glue_tx_channel *tx_chn);
void k3_udma_glue_disable_tx_chn(struct k3_udma_glue_tx_channel *tx_chn);
void k3_udma_glue_tdown_tx_chn(struct k3_udma_glue_tx_channel *tx_chn,
bool sync);
void k3_udma_glue_reset_tx_chn(struct k3_udma_glue_tx_channel *tx_chn,
void *data, void (*cleanup)(void *data, dma_addr_t desc_dma));
u32 k3_udma_glue_tx_get_hdesc_size(struct k3_udma_glue_tx_channel *tx_chn);
u32 k3_udma_glue_tx_get_txcq_id(struct k3_udma_glue_tx_channel *tx_chn);
int k3_udma_glue_tx_get_irq(struct k3_udma_glue_tx_channel *tx_chn);
enum {
K3_UDMA_GLUE_SRC_TAG_LO_KEEP = 0,
K3_UDMA_GLUE_SRC_TAG_LO_USE_FLOW_REG = 1,
K3_UDMA_GLUE_SRC_TAG_LO_USE_REMOTE_FLOW_ID = 2,
K3_UDMA_GLUE_SRC_TAG_LO_USE_REMOTE_SRC_TAG = 4,
};
/**
* k3_udma_glue_rx_flow_cfg - UDMA RX flow cfg
*
* @rx_cfg: RX ring configuration
* @rxfdq_cfg: RX free Host PD ring configuration
* @ring_rxq_id: RX ring id (or -1 for any)
* @ring_rxfdq0_id: RX free Host PD ring (FDQ) if (or -1 for any)
* @rx_error_handling: Rx Error Handling Mode (0 - drop, 1 - re-try)
* @src_tag_lo_sel: Rx Source Tag Low Byte Selector in Host PD
*/
struct k3_udma_glue_rx_flow_cfg {
struct k3_ring_cfg rx_cfg;
struct k3_ring_cfg rxfdq_cfg;
int ring_rxq_id;
int ring_rxfdq0_id;
bool rx_error_handling;
int src_tag_lo_sel;
};
/**
* k3_udma_glue_rx_channel_cfg - UDMA RX channel cfg
*
* @psdata_size: SW Data is present in Host PD of @swdata_size bytes
* @flow_id_base: first flow_id used by channel.
* if @flow_id_base = -1 - range of GP rflows will be
* allocated dynamically.
* @flow_id_num: number of RX flows used by channel
* @flow_id_use_rxchan_id: use RX channel id as flow id,
* used only if @flow_id_num = 1
* @remote indication that RX channel is remote - some remote CPU
* core owns and control the RX channel. Linux Host only
* allowed to attach and configure RX Flow within RX
* channel. if set - not RX channel operation will be
* performed by K3 NAVSS DMA glue interface.
* @def_flow_cfg default RX flow configuration,
* used only if @flow_id_num = 1
*/
struct k3_udma_glue_rx_channel_cfg {
u32 swdata_size;
int flow_id_base;
int flow_id_num;
bool flow_id_use_rxchan_id;
bool remote;
struct k3_udma_glue_rx_flow_cfg *def_flow_cfg;
};
struct k3_udma_glue_rx_channel;
struct k3_udma_glue_rx_channel *k3_udma_glue_request_rx_chn(
struct device *dev,
const char *name,
struct k3_udma_glue_rx_channel_cfg *cfg);
void k3_udma_glue_release_rx_chn(struct k3_udma_glue_rx_channel *rx_chn);
int k3_udma_glue_enable_rx_chn(struct k3_udma_glue_rx_channel *rx_chn);
void k3_udma_glue_disable_rx_chn(struct k3_udma_glue_rx_channel *rx_chn);
void k3_udma_glue_tdown_rx_chn(struct k3_udma_glue_rx_channel *rx_chn,
bool sync);
int k3_udma_glue_push_rx_chn(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_num, struct cppi5_host_desc_t *desc_tx,
dma_addr_t desc_dma);
int k3_udma_glue_pop_rx_chn(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_num, dma_addr_t *desc_dma);
int k3_udma_glue_rx_flow_init(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_idx, struct k3_udma_glue_rx_flow_cfg *flow_cfg);
u32 k3_udma_glue_rx_flow_get_fdq_id(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_idx);
u32 k3_udma_glue_rx_get_flow_id_base(struct k3_udma_glue_rx_channel *rx_chn);
int k3_udma_glue_rx_get_irq(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_num);
void k3_udma_glue_rx_put_irq(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_num);
void k3_udma_glue_reset_rx_chn(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_num, void *data,
void (*cleanup)(void *data, dma_addr_t desc_dma),
bool skip_fdq);
int k3_udma_glue_rx_flow_enable(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_idx);
int k3_udma_glue_rx_flow_disable(struct k3_udma_glue_rx_channel *rx_chn,
u32 flow_idx);
#endif /* K3_UDMA_GLUE_H_ */