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 <vigne...@ti.com> Reviewed-by: Grygorii Strashko <grygorii.stras...@ti.com> --- drivers/dma/ti/k3-udma.c | 293 ++++++++++++++++++++++++++++----------- 1 file changed, 214 insertions(+), 79 deletions(-) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 2e64d338caaf..360f69651680 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 @@ 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; }; @@ -201,19 +221,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) @@ -536,6 +562,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) \ @@ -558,7 +606,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) { @@ -844,6 +891,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; @@ -855,7 +903,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) @@ -866,7 +914,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); @@ -881,6 +929,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; @@ -892,7 +941,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) { @@ -912,7 +961,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); @@ -937,7 +986,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) @@ -963,7 +1012,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); @@ -1104,16 +1154,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) @@ -1132,79 +1300,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]; -- 2.24.0