dma: ti: k3-udma: Query DMA channels allocated from Resource Manager
authorVignesh Raghavendra <vigneshr@ti.com>
Mon, 9 Dec 2019 04:55:32 +0000 (10:25 +0530)
committerLokesh Vutla <lokeshvutla@ti.com>
Mon, 20 Jan 2020 04:40:28 +0000 (10:10 +0530)
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>
drivers/dma/ti/k3-udma.c

index f7128610c570e164f63168d8e440cb5a0186e2a8..605ac06ea198e05e4a980fbb3d6cec214389b872 100644 (file)
 #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];