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>
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index f712861..605ac06 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -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 @@
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 @@
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 @@
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 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
return 0;
}
+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);
+
-#define UDMA_MAX_CHANNELS 192
+ /* 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 @@
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];