dma: ti: k3-udma: Query DMA channels allocated from Resource Manager

On K3 SoCs, DMA channels are shared across multiple entities, therefore
U-Boot DMA driver needs to query resource range from centralised
resource management controller i.e SystemFirmware and use DMA channels
allocated for A72 host. Add support for the same.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Reviewed-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
This commit is contained in:
Vignesh Raghavendra 2019-12-09 10:25:32 +05:30 committed by Lokesh Vutla
parent 016e6d6a4f
commit a8837cf438
1 changed files with 214 additions and 79 deletions

View File

@ -12,12 +12,14 @@
#include <malloc.h>
#include <asm/dma-mapping.h>
#include <dm.h>
#include <dm/device.h>
#include <dm/read.h>
#include <dm/of_access.h>
#include <dma.h>
#include <dma-uclass.h>
#include <linux/delay.h>
#include <dt-bindings/dma/k3-udma.h>
#include <linux/bitmap.h>
#include <linux/soc/ti/k3-navss-ringacc.h>
#include <linux/soc/ti/cppi5.h>
#include <linux/soc/ti/ti-udma.h>
@ -31,6 +33,8 @@
#define RINGACC_RING_USE_PROXY (1)
#endif
#define K3_UDMA_MAX_RFLOWS 1024
struct udma_chan;
enum udma_mmr {
@ -64,10 +68,30 @@ struct udma_rflow {
int id;
};
enum udma_rm_range {
RM_RANGE_TCHAN = 0,
RM_RANGE_RCHAN,
RM_RANGE_RFLOW,
RM_RANGE_LAST,
};
struct udma_tisci_rm {
const struct ti_sci_handle *tisci;
const struct ti_sci_rm_udmap_ops *tisci_udmap_ops;
u32 tisci_dev_id;
/* tisci information for PSI-L thread pairing/unpairing */
const struct ti_sci_rm_psil_ops *tisci_psil_ops;
u32 tisci_navss_dev_id;
struct ti_sci_resource *rm_ranges[RM_RANGE_LAST];
};
struct udma_dev {
struct device *dev;
struct udevice *dev;
void __iomem *mmrs[MMR_LAST];
struct udma_tisci_rm tisci_rm;
struct k3_nav_ringacc *ringacc;
u32 features;
@ -79,6 +103,7 @@ struct udma_dev {
unsigned long *tchan_map;
unsigned long *rchan_map;
unsigned long *rflow_map;
unsigned long *rflow_map_reserved;
struct udma_tchan *tchans;
struct udma_rchan *rchans;
@ -88,11 +113,6 @@ struct udma_dev {
u32 psil_base;
u32 ch_count;
const struct ti_sci_handle *tisci;
const struct ti_sci_rm_udmap_ops *tisci_udmap_ops;
const struct ti_sci_rm_psil_ops *tisci_psil_ops;
u32 tisci_dev_id;
u32 tisci_navss_dev_id;
bool is_coherent;
};
@ -203,19 +223,25 @@ static inline void udma_rchanrt_write(struct udma_rchan *rchan,
static inline int udma_navss_psil_pair(struct udma_dev *ud, u32 src_thread,
u32 dst_thread)
{
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
dst_thread |= UDMA_PSIL_DST_THREAD_ID_OFFSET;
return ud->tisci_psil_ops->pair(ud->tisci,
ud->tisci_navss_dev_id,
src_thread, dst_thread);
return tisci_rm->tisci_psil_ops->pair(tisci_rm->tisci,
tisci_rm->tisci_navss_dev_id,
src_thread, dst_thread);
}
static inline int udma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
u32 dst_thread)
{
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
dst_thread |= UDMA_PSIL_DST_THREAD_ID_OFFSET;
return ud->tisci_psil_ops->unpair(ud->tisci,
ud->tisci_navss_dev_id,
src_thread, dst_thread);
return tisci_rm->tisci_psil_ops->unpair(tisci_rm->tisci,
tisci_rm->tisci_navss_dev_id,
src_thread, dst_thread);
}
static inline char *udma_get_dir_text(enum dma_direction dir)
@ -538,6 +564,28 @@ static void udma_poll_completion(struct udma_chan *uc, dma_addr_t *paddr)
}
}
static struct udma_rflow *__udma_reserve_rflow(struct udma_dev *ud, int id)
{
DECLARE_BITMAP(tmp, K3_UDMA_MAX_RFLOWS);
if (id >= 0) {
if (test_bit(id, ud->rflow_map)) {
dev_err(ud->dev, "rflow%d is in use\n", id);
return ERR_PTR(-ENOENT);
}
} else {
bitmap_or(tmp, ud->rflow_map, ud->rflow_map_reserved,
ud->rflow_cnt);
id = find_next_zero_bit(tmp, ud->rflow_cnt, ud->rchan_cnt);
if (id >= ud->rflow_cnt)
return ERR_PTR(-ENOENT);
}
__set_bit(id, ud->rflow_map);
return &ud->rflows[id];
}
#define UDMA_RESERVE_RESOURCE(res) \
static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud, \
int id) \
@ -560,7 +608,6 @@ static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud, \
UDMA_RESERVE_RESOURCE(tchan);
UDMA_RESERVE_RESOURCE(rchan);
UDMA_RESERVE_RESOURCE(rflow);
static int udma_get_tchan(struct udma_chan *uc)
{
@ -846,6 +893,7 @@ static int udma_alloc_tchan_sci_req(struct udma_chan *uc)
struct udma_dev *ud = uc->ud;
int tc_ring = k3_nav_ringacc_get_ring_id(uc->tchan->tc_ring);
struct ti_sci_msg_rm_udmap_tx_ch_cfg req;
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
u32 mode;
int ret;
@ -857,7 +905,7 @@ static int udma_alloc_tchan_sci_req(struct udma_chan *uc)
req.valid_params = TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID |
TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID |
TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID;
req.nav_id = ud->tisci_dev_id;
req.nav_id = tisci_rm->tisci_dev_id;
req.index = uc->tchan->id;
req.tx_chan_type = mode;
if (uc->dir == DMA_MEM_TO_MEM)
@ -868,7 +916,7 @@ static int udma_alloc_tchan_sci_req(struct udma_chan *uc)
0) >> 2;
req.txcq_qnum = tc_ring;
ret = ud->tisci_udmap_ops->tx_ch_cfg(ud->tisci, &req);
ret = tisci_rm->tisci_udmap_ops->tx_ch_cfg(tisci_rm->tisci, &req);
if (ret)
dev_err(ud->dev, "tisci tx alloc failed %d\n", ret);
@ -883,6 +931,7 @@ static int udma_alloc_rchan_sci_req(struct udma_chan *uc)
int tc_ring = k3_nav_ringacc_get_ring_id(uc->tchan->tc_ring);
struct ti_sci_msg_rm_udmap_rx_ch_cfg req = { 0 };
struct ti_sci_msg_rm_udmap_flow_cfg flow_req = { 0 };
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
u32 mode;
int ret;
@ -894,7 +943,7 @@ static int udma_alloc_rchan_sci_req(struct udma_chan *uc)
req.valid_params = TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID |
TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID |
TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID;
req.nav_id = ud->tisci_dev_id;
req.nav_id = tisci_rm->tisci_dev_id;
req.index = uc->rchan->id;
req.rx_chan_type = mode;
if (uc->dir == DMA_MEM_TO_MEM) {
@ -914,7 +963,7 @@ static int udma_alloc_rchan_sci_req(struct udma_chan *uc)
TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID;
}
ret = ud->tisci_udmap_ops->rx_ch_cfg(ud->tisci, &req);
ret = tisci_rm->tisci_udmap_ops->rx_ch_cfg(tisci_rm->tisci, &req);
if (ret) {
dev_err(ud->dev, "tisci rx %u cfg failed %d\n",
uc->rchan->id, ret);
@ -939,7 +988,7 @@ static int udma_alloc_rchan_sci_req(struct udma_chan *uc)
TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_FDQ3_QNUM_VALID |
TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_PS_LOCATION_VALID;
flow_req.nav_id = ud->tisci_dev_id;
flow_req.nav_id = tisci_rm->tisci_dev_id;
flow_req.flow_index = uc->rflow->id;
if (uc->needs_epib)
@ -965,7 +1014,8 @@ static int udma_alloc_rchan_sci_req(struct udma_chan *uc)
flow_req.rx_fdq3_qnum = fd_ring;
flow_req.rx_ps_location = 0;
ret = ud->tisci_udmap_ops->rx_flow_cfg(ud->tisci, &flow_req);
ret = tisci_rm->tisci_udmap_ops->rx_flow_cfg(tisci_rm->tisci,
&flow_req);
if (ret)
dev_err(ud->dev, "tisci rx %u flow %u cfg failed %d\n",
uc->rchan->id, uc->rflow->id, ret);
@ -1106,16 +1156,134 @@ static int udma_get_mmrs(struct udevice *dev)
return 0;
}
#define UDMA_MAX_CHANNELS 192
static int udma_setup_resources(struct udma_dev *ud)
{
struct udevice *dev = ud->dev;
int ch_count, i;
u32 cap2, cap3;
struct ti_sci_resource_desc *rm_desc;
struct ti_sci_resource *rm_res;
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
static const char * const range_names[] = { "ti,sci-rm-range-tchan",
"ti,sci-rm-range-rchan",
"ti,sci-rm-range-rflow" };
cap2 = udma_read(ud->mmrs[MMR_GCFG], 0x28);
cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
ud->rflow_cnt = cap3 & 0x3fff;
ud->tchan_cnt = cap2 & 0x1ff;
ud->echan_cnt = (cap2 >> 9) & 0x1ff;
ud->rchan_cnt = (cap2 >> 18) & 0x1ff;
ch_count = ud->tchan_cnt + ud->rchan_cnt;
ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans),
GFP_KERNEL);
ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans),
GFP_KERNEL);
ud->rflow_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rflow_cnt),
sizeof(unsigned long), GFP_KERNEL);
ud->rflow_map_reserved = devm_kcalloc(dev, BITS_TO_LONGS(ud->rflow_cnt),
sizeof(unsigned long),
GFP_KERNEL);
ud->rflows = devm_kcalloc(dev, ud->rflow_cnt, sizeof(*ud->rflows),
GFP_KERNEL);
if (!ud->tchan_map || !ud->rchan_map || !ud->rflow_map ||
!ud->rflow_map_reserved || !ud->tchans || !ud->rchans ||
!ud->rflows)
return -ENOMEM;
/*
* RX flows with the same Ids as RX channels are reserved to be used
* as default flows if remote HW can't generate flow_ids. Those
* RX flows can be requested only explicitly by id.
*/
bitmap_set(ud->rflow_map_reserved, 0, ud->rchan_cnt);
/* Get resource ranges from tisci */
for (i = 0; i < RM_RANGE_LAST; i++)
tisci_rm->rm_ranges[i] =
devm_ti_sci_get_of_resource(tisci_rm->tisci, dev,
tisci_rm->tisci_dev_id,
(char *)range_names[i]);
/* tchan ranges */
rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
if (IS_ERR(rm_res)) {
bitmap_zero(ud->tchan_map, ud->tchan_cnt);
} else {
bitmap_fill(ud->tchan_map, ud->tchan_cnt);
for (i = 0; i < rm_res->sets; i++) {
rm_desc = &rm_res->desc[i];
bitmap_clear(ud->tchan_map, rm_desc->start,
rm_desc->num);
}
}
/* rchan and matching default flow ranges */
rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
if (IS_ERR(rm_res)) {
bitmap_zero(ud->rchan_map, ud->rchan_cnt);
bitmap_zero(ud->rflow_map, ud->rchan_cnt);
} else {
bitmap_fill(ud->rchan_map, ud->rchan_cnt);
bitmap_fill(ud->rflow_map, ud->rchan_cnt);
for (i = 0; i < rm_res->sets; i++) {
rm_desc = &rm_res->desc[i];
bitmap_clear(ud->rchan_map, rm_desc->start,
rm_desc->num);
bitmap_clear(ud->rflow_map, rm_desc->start,
rm_desc->num);
}
}
/* GP rflow ranges */
rm_res = tisci_rm->rm_ranges[RM_RANGE_RFLOW];
if (IS_ERR(rm_res)) {
bitmap_clear(ud->rflow_map, ud->rchan_cnt,
ud->rflow_cnt - ud->rchan_cnt);
} else {
bitmap_set(ud->rflow_map, ud->rchan_cnt,
ud->rflow_cnt - ud->rchan_cnt);
for (i = 0; i < rm_res->sets; i++) {
rm_desc = &rm_res->desc[i];
bitmap_clear(ud->rflow_map, rm_desc->start,
rm_desc->num);
}
}
ch_count -= bitmap_weight(ud->tchan_map, ud->tchan_cnt);
ch_count -= bitmap_weight(ud->rchan_map, ud->rchan_cnt);
if (!ch_count)
return -ENODEV;
ud->channels = devm_kcalloc(dev, ch_count, sizeof(*ud->channels),
GFP_KERNEL);
if (!ud->channels)
return -ENOMEM;
dev_info(dev,
"Channels: %d (tchan: %u, echan: %u, rchan: %u, rflow: %u)\n",
ch_count, ud->tchan_cnt, ud->echan_cnt, ud->rchan_cnt,
ud->rflow_cnt);
return ch_count;
}
static int udma_probe(struct udevice *dev)
{
struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct udma_dev *ud = dev_get_priv(dev);
int i, ret;
u32 cap2, cap3;
struct udevice *tmp;
struct udevice *tisci_dev = NULL;
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
ofnode navss_ofnode = ofnode_get_parent(dev_ofnode(dev));
ret = udma_get_mmrs(dev);
if (ret)
@ -1134,79 +1302,46 @@ static int udma_probe(struct udevice *dev)
return -EINVAL;
}
ret = uclass_get_device_by_name(UCLASS_FIRMWARE, "dmsc", &tisci_dev);
ret = uclass_get_device_by_phandle(UCLASS_FIRMWARE, dev,
"ti,sci", &tisci_dev);
if (ret) {
debug("TISCI RA RM get failed (%d)\n", ret);
ud->tisci = NULL;
return 0;
debug("Failed to get TISCI phandle (%d)\n", ret);
tisci_rm->tisci = NULL;
return -EINVAL;
}
ud->tisci = (struct ti_sci_handle *)
(ti_sci_get_handle_from_sysfw(tisci_dev));
tisci_rm->tisci = (struct ti_sci_handle *)
(ti_sci_get_handle_from_sysfw(tisci_dev));
ret = dev_read_u32_default(dev, "ti,sci", 0);
if (!ret) {
dev_err(dev, "TISCI RA RM disabled\n");
ud->tisci = NULL;
tisci_rm->tisci_dev_id = -1;
ret = dev_read_u32(dev, "ti,sci-dev-id", &tisci_rm->tisci_dev_id);
if (ret) {
dev_err(dev, "ti,sci-dev-id read failure %d\n", ret);
return ret;
}
if (ud->tisci) {
ofnode navss_ofnode = ofnode_get_parent(dev_ofnode(dev));
ud->tisci_dev_id = -1;
ret = dev_read_u32(dev, "ti,sci-dev-id", &ud->tisci_dev_id);
if (ret) {
dev_err(dev, "ti,sci-dev-id read failure %d\n", ret);
return ret;
}
ud->tisci_navss_dev_id = -1;
ret = ofnode_read_u32(navss_ofnode, "ti,sci-dev-id",
&ud->tisci_navss_dev_id);
if (ret) {
dev_err(dev, "navss sci-dev-id read failure %d\n", ret);
return ret;
}
ud->tisci_udmap_ops = &ud->tisci->ops.rm_udmap_ops;
ud->tisci_psil_ops = &ud->tisci->ops.rm_psil_ops;
tisci_rm->tisci_navss_dev_id = -1;
ret = ofnode_read_u32(navss_ofnode, "ti,sci-dev-id",
&tisci_rm->tisci_navss_dev_id);
if (ret) {
dev_err(dev, "navss sci-dev-id read failure %d\n", ret);
return ret;
}
ud->is_coherent = dev_read_bool(dev, "dma-coherent");
tisci_rm->tisci_udmap_ops = &tisci_rm->tisci->ops.rm_udmap_ops;
tisci_rm->tisci_psil_ops = &tisci_rm->tisci->ops.rm_psil_ops;
cap2 = udma_read(ud->mmrs[MMR_GCFG], 0x28);
cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
ud->rflow_cnt = cap3 & 0x3fff;
ud->tchan_cnt = cap2 & 0x1ff;
ud->echan_cnt = (cap2 >> 9) & 0x1ff;
ud->rchan_cnt = (cap2 >> 18) & 0x1ff;
ud->ch_count = ud->tchan_cnt + ud->rchan_cnt;
ud->dev = dev;
ud->ch_count = udma_setup_resources(ud);
if (ud->ch_count <= 0)
return ud->ch_count;
dev_info(dev,
"Number of channels: %u (tchan: %u, echan: %u, rchan: %u dev-id %u)\n",
ud->ch_count, ud->tchan_cnt, ud->echan_cnt, ud->rchan_cnt,
ud->tisci_dev_id);
tisci_rm->tisci_dev_id);
dev_info(dev, "Number of rflows: %u\n", ud->rflow_cnt);
ud->channels = devm_kcalloc(dev, ud->ch_count, sizeof(*ud->channels),
GFP_KERNEL);
ud->tchan_map = devm_kcalloc(dev, BITS_TO_LONGS(ud->tchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
ud->tchans = devm_kcalloc(dev, ud->tchan_cnt,
sizeof(*ud->tchans), GFP_KERNEL);
ud->rchan_map = devm_kcalloc(dev, BITS_TO_LONGS(ud->rchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
ud->rchans = devm_kcalloc(dev, ud->rchan_cnt,
sizeof(*ud->rchans), GFP_KERNEL);
ud->rflow_map = devm_kcalloc(dev, BITS_TO_LONGS(ud->rflow_cnt),
sizeof(unsigned long), GFP_KERNEL);
ud->rflows = devm_kcalloc(dev, ud->rflow_cnt,
sizeof(*ud->rflows), GFP_KERNEL);
if (!ud->channels || !ud->tchan_map || !ud->rchan_map ||
!ud->rflow_map || !ud->tchans || !ud->rchans || !ud->rflows)
return -ENOMEM;
for (i = 0; i < ud->tchan_cnt; i++) {
struct udma_tchan *tchan = &ud->tchans[i];