diff mbox series

[02/12] soc: ti: k3-navss-ringacc: Add AM64 ringacc support

Message ID 20210510143613.16512-3-vigneshr@ti.com
State Accepted
Commit c409b4932dba6403a706f9453d627abfb5d93e20
Delegated to: Lokesh Vutla
Headers show
Series AM64x: DMA and ethernet support | expand

Commit Message

Raghavendra, Vignesh May 10, 2021, 2:36 p.m. UTC
AM64 dual mode rings are modeled as pair of Rings objects which has common
configuration and memory buffer, but separate real-time control register
sets for each direction mem2dev (forward) and dev2mem (reverse).

AM64 rings must be requested only using k3_ringacc_request_rings_pair(),
and forward ring must always be initialized/configured. After this any
other Ringacc APIs can be used without any callers changes.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
 drivers/soc/ti/k3-navss-ringacc.c       | 273 +++++++++++++++++++++++-
 include/linux/soc/ti/k3-navss-ringacc.h |  15 ++
 2 files changed, 283 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/drivers/soc/ti/k3-navss-ringacc.c b/drivers/soc/ti/k3-navss-ringacc.c
index c5099ad084..10ef6be720 100644
--- a/drivers/soc/ti/k3-navss-ringacc.c
+++ b/drivers/soc/ti/k3-navss-ringacc.c
@@ -23,6 +23,7 @@ 
 #include <linux/err.h>
 #include <linux/soc/ti/k3-navss-ringacc.h>
 #include <linux/soc/ti/ti_sci_protocol.h>
+#include <linux/soc/ti/cppi5.h>
 
 #define set_bit(bit, bitmap)	__set_bit(bit, bitmap)
 #define clear_bit(bit, bitmap)	__clear_bit(bit, bitmap)
@@ -56,6 +57,7 @@  static	u32 ringacc_readl(void __iomem *reg)
 }
 
 #define KNAV_RINGACC_CFG_RING_SIZE_ELCNT_MASK		GENMASK(19, 0)
+#define K3_DMARING_RING_CFG_RING_SIZE_ELCNT_MASK		GENMASK(15, 0)
 
 /**
  * struct k3_nav_ring_rt_regs -  The RA Control/Status Registers region
@@ -71,6 +73,13 @@  struct k3_nav_ring_rt_regs {
 };
 
 #define KNAV_RINGACC_RT_REGS_STEP	0x1000
+#define K3_DMARING_RING_RT_REGS_STEP			0x2000
+#define K3_DMARING_RING_RT_REGS_REVERSE_OFS		0x1000
+#define KNAV_RINGACC_RT_OCC_MASK		GENMASK(20, 0)
+#define K3_DMARING_RING_RT_OCC_TDOWN_COMPLETE		BIT(31)
+#define K3_DMARING_RING_RT_DB_ENTRY_MASK		GENMASK(7, 0)
+#define K3_DMARING_RING_RT_DB_TDOWN_ACK		BIT(31)
+
 
 /**
  * struct k3_nav_ring_fifo_regs -  The Ring Accelerator Queues Registers region
@@ -173,6 +182,7 @@  struct k3_nav_ring {
 	u32		flags;
 #define KNAV_RING_FLAG_BUSY	BIT(1)
 #define K3_NAV_RING_FLAG_SHARED	BIT(2)
+#define K3_NAV_RING_FLAG_REVERSE BIT(3)
 	struct k3_nav_ring_state state;
 	u32		ring_id;
 	struct k3_nav_ringacc	*parent;
@@ -200,6 +210,7 @@  struct k3_nav_ringacc_ops {
  * @tisci_ring_ops - ti-sci rings ops
  * @tisci_dev_id - ti-sci device id
  * @ops: SoC specific ringacc operation
+ * @dual_ring: indicate k3_dmaring dual ring support
  */
 struct k3_nav_ringacc {
 	struct udevice *dev;
@@ -220,8 +231,24 @@  struct k3_nav_ringacc {
 	u32  tisci_dev_id;
 
 	const struct k3_nav_ringacc_ops *ops;
+	bool dual_ring;
 };
 
+static int k3_nav_ringacc_ring_read_occ(struct k3_nav_ring *ring)
+{
+	return readl(&ring->rt->occ) & KNAV_RINGACC_RT_OCC_MASK;
+}
+
+static void k3_nav_ringacc_ring_update_occ(struct k3_nav_ring *ring)
+{
+	u32 val;
+
+	val = readl(&ring->rt->occ);
+
+	ring->state.occ = val & KNAV_RINGACC_RT_OCC_MASK;
+	ring->state.tdown_complete = !!(val & K3_DMARING_RING_RT_OCC_TDOWN_COMPLETE);
+}
+
 static long k3_nav_ringacc_ring_get_fifo_pos(struct k3_nav_ring *ring)
 {
 	return KNAV_RINGACC_FIFO_WINDOW_SIZE_BYTES -
@@ -235,12 +262,23 @@  static void *k3_nav_ringacc_get_elm_addr(struct k3_nav_ring *ring, u32 idx)
 
 static int k3_nav_ringacc_ring_push_mem(struct k3_nav_ring *ring, void *elem);
 static int k3_nav_ringacc_ring_pop_mem(struct k3_nav_ring *ring, void *elem);
+static int k3_dmaring_ring_fwd_pop_mem(struct k3_nav_ring *ring, void *elem);
+static int k3_dmaring_ring_reverse_pop_mem(struct k3_nav_ring *ring, void *elem);
 
 static struct k3_nav_ring_ops k3_nav_mode_ring_ops = {
 		.push_tail = k3_nav_ringacc_ring_push_mem,
 		.pop_head = k3_nav_ringacc_ring_pop_mem,
 };
 
+static struct k3_nav_ring_ops k3_dmaring_fwd_ring_ops = {
+		.push_tail = k3_nav_ringacc_ring_push_mem,
+		.pop_head = k3_dmaring_ring_fwd_pop_mem,
+};
+
+static struct k3_nav_ring_ops k3_dmaring_reverse_ring_ops = {
+		.pop_head = k3_dmaring_ring_reverse_pop_mem,
+};
+
 static int k3_nav_ringacc_ring_push_io(struct k3_nav_ring *ring, void *elem);
 static int k3_nav_ringacc_ring_pop_io(struct k3_nav_ring *ring, void *elem);
 static int k3_nav_ringacc_ring_push_head_io(struct k3_nav_ring *ring,
@@ -328,6 +366,27 @@  error:
 	return NULL;
 }
 
+static int k3_dmaring_ring_request_rings_pair(struct k3_nav_ringacc *ringacc,
+					      int fwd_id, int compl_id,
+					      struct k3_nav_ring **fwd_ring,
+					      struct k3_nav_ring **compl_ring)
+{
+	/* k3_dmaring: fwd_id == compl_id, so we ignore compl_id */
+	if (fwd_id < 0)
+		return -EINVAL;
+
+	if (test_bit(fwd_id, ringacc->rings_inuse))
+		return -EBUSY;
+
+	*fwd_ring = &ringacc->rings[fwd_id];
+	*compl_ring = &ringacc->rings[fwd_id + ringacc->num_rings];
+	set_bit(fwd_id, ringacc->rings_inuse);
+	ringacc->rings[fwd_id].use_count++;
+	dev_dbg(ringacc->dev, "Giving ring#%d\n", fwd_id);
+
+	return 0;
+}
+
 int k3_nav_ringacc_request_rings_pair(struct k3_nav_ringacc *ringacc,
 				      int fwd_id, int compl_id,
 				      struct k3_nav_ring **fwd_ring,
@@ -338,6 +397,10 @@  int k3_nav_ringacc_request_rings_pair(struct k3_nav_ringacc *ringacc,
 	if (!fwd_ring || !compl_ring)
 		return -EINVAL;
 
+	if (ringacc->dual_ring)
+		return k3_dmaring_ring_request_rings_pair(ringacc, fwd_id, compl_id,
+						    fwd_ring, compl_ring);
+
 	*fwd_ring = k3_nav_ringacc_request_ring(ringacc, fwd_id, 0);
 	if (!(*fwd_ring))
 		return -ENODEV;
@@ -493,6 +556,13 @@  int k3_nav_ringacc_ring_free(struct k3_nav_ring *ring)
 
 	ringacc = ring->parent;
 
+	/*
+	 * k3_dmaring: rings shared memory and configuration, only forward ring is
+	 * configured and reverse ring considered as slave.
+	 */
+	if (ringacc->dual_ring && (ring->flags & K3_NAV_RING_FLAG_REVERSE))
+		return 0;
+
 	pr_debug("%s flags: 0x%08x\n", __func__, ring->flags);
 
 	if (!test_bit(ring->ring_id, ringacc->rings_inuse))
@@ -562,6 +632,76 @@  static int k3_nav_ringacc_ring_cfg_sci(struct k3_nav_ring *ring)
 	return ret;
 }
 
+static int k3_dmaring_ring_cfg(struct k3_nav_ring *ring, struct k3_nav_ring_cfg *cfg)
+{
+	struct k3_nav_ringacc *ringacc;
+	struct k3_nav_ring *reverse_ring;
+	int ret = 0;
+
+	if (cfg->elm_size != K3_NAV_RINGACC_RING_ELSIZE_8 ||
+	    cfg->mode != K3_NAV_RINGACC_RING_MODE_RING ||
+	    cfg->size & ~K3_DMARING_RING_CFG_RING_SIZE_ELCNT_MASK)
+		return -EINVAL;
+
+	ringacc = ring->parent;
+
+	/*
+	 * k3_dmaring: rings shared memory and configuration, only forward ring is
+	 * configured and reverse ring considered as slave.
+	 */
+	if (ringacc->dual_ring && (ring->flags & K3_NAV_RING_FLAG_REVERSE))
+		return 0;
+
+	if (!test_bit(ring->ring_id, ringacc->rings_inuse))
+		return -EINVAL;
+
+	ring->size = cfg->size;
+	ring->elm_size = cfg->elm_size;
+	ring->mode = cfg->mode;
+	memset(&ring->state, 0, sizeof(ring->state));
+
+	ring->ops = &k3_dmaring_fwd_ring_ops;
+
+	ring->ring_mem_virt =
+		dma_alloc_coherent(ring->size * (4 << ring->elm_size),
+				   (unsigned long *)&ring->ring_mem_dma);
+	if (!ring->ring_mem_virt) {
+		dev_err(ringacc->dev, "Failed to alloc ring mem\n");
+		ret = -ENOMEM;
+		goto err_free_ops;
+	}
+
+	ret = k3_nav_ringacc_ring_cfg_sci(ring);
+	if (ret)
+		goto err_free_mem;
+
+	ring->flags |= KNAV_RING_FLAG_BUSY;
+
+	/* k3_dmaring: configure reverse ring */
+	reverse_ring = &ringacc->rings[ring->ring_id + ringacc->num_rings];
+	reverse_ring->size = cfg->size;
+	reverse_ring->elm_size = cfg->elm_size;
+	reverse_ring->mode = cfg->mode;
+	memset(&reverse_ring->state, 0, sizeof(reverse_ring->state));
+	reverse_ring->ops = &k3_dmaring_reverse_ring_ops;
+
+	reverse_ring->ring_mem_virt = ring->ring_mem_virt;
+	reverse_ring->ring_mem_dma = ring->ring_mem_dma;
+	reverse_ring->flags |= KNAV_RING_FLAG_BUSY;
+
+	return 0;
+
+err_free_mem:
+	dma_free_coherent(ringacc->dev,
+			  ring->size * (4 << ring->elm_size),
+			  ring->ring_mem_virt,
+			  ring->ring_mem_dma);
+err_free_ops:
+	ring->ops = NULL;
+	ring->proxy = NULL;
+	return ret;
+}
+
 int k3_nav_ringacc_ring_cfg(struct k3_nav_ring *ring,
 			    struct k3_nav_ring_cfg *cfg)
 {
@@ -570,6 +710,10 @@  int k3_nav_ringacc_ring_cfg(struct k3_nav_ring *ring,
 
 	if (!ring || !cfg)
 		return -EINVAL;
+
+	if (ringacc->dual_ring)
+		return k3_dmaring_ring_cfg(ring, cfg);
+
 	if (cfg->elm_size > K3_NAV_RINGACC_RING_ELSIZE_256 ||
 	    cfg->mode > K3_NAV_RINGACC_RING_MODE_QM ||
 	    cfg->size & ~KNAV_RINGACC_CFG_RING_SIZE_ELCNT_MASK ||
@@ -839,6 +983,63 @@  static int k3_nav_ringacc_ring_pop_tail_io(struct k3_nav_ring *ring, void *elem)
 			ring, elem, K3_RINGACC_ACCESS_MODE_POP_HEAD);
 }
 
+static int k3_dmaring_ring_fwd_pop_mem(struct k3_nav_ring *ring, void *elem)
+{
+	void *elem_ptr;
+	u32 elem_idx;
+
+	/*
+	 * k3_dmaring: forward ring is always tied DMA channel and HW does not
+	 * maintain any state data required for POP operation and its unknown
+	 * how much elements were consumed by HW. So, to actually
+	 * do POP, the read pointer has to be recalculated every time.
+	 */
+	ring->state.occ = k3_nav_ringacc_ring_read_occ(ring);
+	if (ring->state.windex >= ring->state.occ)
+		elem_idx = ring->state.windex - ring->state.occ;
+	else
+		elem_idx = ring->size - (ring->state.occ - ring->state.windex);
+
+	elem_ptr = k3_nav_ringacc_get_elm_addr(ring, elem_idx);
+	invalidate_dcache_range((unsigned long)ring->ring_mem_virt,
+				ALIGN((unsigned long)ring->ring_mem_virt +
+				      ring->size * (4 << ring->elm_size),
+				      ARCH_DMA_MINALIGN));
+
+	memcpy(elem, elem_ptr, (4 << ring->elm_size));
+
+	ring->state.occ--;
+	writel(-1, &ring->rt->db);
+
+	dev_dbg(ring->parent->dev, "%s: occ%d Windex%d Rindex%d pos_ptr%px\n",
+		__func__, ring->state.occ, ring->state.windex, elem_idx,
+		elem_ptr);
+	return 0;
+}
+
+static int k3_dmaring_ring_reverse_pop_mem(struct k3_nav_ring *ring, void *elem)
+{
+	void *elem_ptr;
+
+	elem_ptr = k3_nav_ringacc_get_elm_addr(ring, ring->state.rindex);
+
+	if (ring->state.occ) {
+		invalidate_dcache_range((unsigned long)ring->ring_mem_virt,
+					ALIGN((unsigned long)ring->ring_mem_virt +
+					ring->size * (4 << ring->elm_size),
+					ARCH_DMA_MINALIGN));
+
+		memcpy(elem, elem_ptr, (4 << ring->elm_size));
+		ring->state.rindex = (ring->state.rindex + 1) % ring->size;
+		ring->state.occ--;
+		writel(-1 & K3_DMARING_RING_RT_DB_ENTRY_MASK, &ring->rt->db);
+	}
+
+	dev_dbg(ring->parent->dev, "%s: occ%d index%d pos_ptr%px\n",
+		__func__, ring->state.occ, ring->state.rindex, elem_ptr);
+	return 0;
+}
+
 static int k3_nav_ringacc_ring_push_mem(struct k3_nav_ring *ring, void *elem)
 {
 	void *elem_ptr;
@@ -930,7 +1131,7 @@  int k3_nav_ringacc_ring_pop(struct k3_nav_ring *ring, void *elem)
 		return -EINVAL;
 
 	if (!ring->state.occ)
-		ring->state.occ = k3_nav_ringacc_ring_get_occ(ring);
+		k3_nav_ringacc_ring_update_occ(ring);
 
 	pr_debug("ring_pop%d: occ%d index%d\n",
 		 ring->ring_id, ring->state.occ, ring->state.rindex);
@@ -952,7 +1153,7 @@  int k3_nav_ringacc_ring_pop_tail(struct k3_nav_ring *ring, void *elem)
 		return -EINVAL;
 
 	if (!ring->state.occ)
-		ring->state.occ = k3_nav_ringacc_ring_get_occ(ring);
+		k3_nav_ringacc_ring_update_occ(ring);
 
 	pr_debug("ring_pop_tail: occ%d index%d\n",
 		 ring->state.occ, ring->state.rindex);
@@ -969,6 +1170,7 @@  int k3_nav_ringacc_ring_pop_tail(struct k3_nav_ring *ring, void *elem)
 static int k3_nav_ringacc_probe_dt(struct k3_nav_ringacc *ringacc)
 {
 	struct udevice *dev = ringacc->dev;
+	struct udevice *devp = dev;
 	struct udevice *tisci_dev = NULL;
 	int ret;
 
@@ -981,7 +1183,7 @@  static int k3_nav_ringacc_probe_dt(struct k3_nav_ringacc *ringacc)
 	ringacc->dma_ring_reset_quirk =
 			dev_read_bool(dev, "ti,dma-ring-reset-quirk");
 
-	ret = uclass_get_device_by_phandle(UCLASS_FIRMWARE, dev,
+	ret = uclass_get_device_by_phandle(UCLASS_FIRMWARE, devp,
 					   "ti,sci", &tisci_dev);
 	if (ret) {
 		pr_debug("TISCI RA RM get failed (%d)\n", ret);
@@ -991,14 +1193,14 @@  static int k3_nav_ringacc_probe_dt(struct k3_nav_ringacc *ringacc)
 	ringacc->tisci = (struct ti_sci_handle *)
 			 (ti_sci_get_handle_from_sysfw(tisci_dev));
 
-	ret = dev_read_u32_default(dev, "ti,sci", 0);
+	ret = dev_read_u32_default(devp, "ti,sci", 0);
 	if (!ret) {
 		dev_err(dev, "TISCI RA RM disabled\n");
 		ringacc->tisci = NULL;
 		return ret;
 	}
 
-	ret = dev_read_u32(dev, "ti,sci-dev-id", &ringacc->tisci_dev_id);
+	ret = dev_read_u32(devp, "ti,sci-dev-id", &ringacc->tisci_dev_id);
 	if (ret) {
 		dev_err(dev, "ti,sci-dev-id read failure %d\n", ret);
 		ringacc->tisci = NULL;
@@ -1089,6 +1291,67 @@  static int k3_nav_ringacc_init(struct udevice *dev, struct k3_nav_ringacc *ringa
 	return 0;
 }
 
+struct k3_nav_ringacc *k3_ringacc_dmarings_init(struct udevice *dev,
+						struct k3_ringacc_init_data *data)
+{
+	struct k3_nav_ringacc *ringacc;
+	void __iomem *base_rt;
+	int i;
+
+	ringacc = devm_kzalloc(dev, sizeof(*ringacc), GFP_KERNEL);
+	if (!ringacc)
+		return ERR_PTR(-ENOMEM);
+
+	ringacc->dual_ring = true;
+
+	ringacc->dev = dev;
+	ringacc->num_rings = data->num_rings;
+	ringacc->tisci = data->tisci;
+	ringacc->tisci_dev_id = data->tisci_dev_id;
+
+	base_rt = (uint32_t *)devfdt_get_addr_name(dev, "ringrt");
+	if (IS_ERR(base_rt))
+		return base_rt;
+
+	ringacc->rings = devm_kzalloc(dev,
+				      sizeof(*ringacc->rings) *
+				      ringacc->num_rings * 2,
+				      GFP_KERNEL);
+	ringacc->rings_inuse = devm_kcalloc(dev,
+					    BITS_TO_LONGS(ringacc->num_rings),
+					    sizeof(unsigned long), GFP_KERNEL);
+
+	if (!ringacc->rings || !ringacc->rings_inuse)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ringacc->num_rings; i++) {
+		struct k3_nav_ring *ring = &ringacc->rings[i];
+
+		ring->rt = base_rt + K3_DMARING_RING_RT_REGS_STEP * i;
+		ring->parent = ringacc;
+		ring->ring_id = i;
+		ring->proxy_id = K3_RINGACC_PROXY_NOT_USED;
+
+		ring = &ringacc->rings[ringacc->num_rings + i];
+		ring->rt = base_rt + K3_DMARING_RING_RT_REGS_STEP * i +
+			   K3_DMARING_RING_RT_REGS_REVERSE_OFS;
+		ring->parent = ringacc;
+		ring->ring_id = i;
+		ring->proxy_id = K3_RINGACC_PROXY_NOT_USED;
+		ring->flags = K3_NAV_RING_FLAG_REVERSE;
+	}
+
+	ringacc->tisci_ring_ops = &ringacc->tisci->ops.rm_ring_ops;
+
+	dev_info(dev, "k3_dmaring Ring probed rings:%u, sci-dev-id:%u\n",
+		 ringacc->num_rings,
+		 ringacc->tisci_dev_id);
+	dev_info(dev, "dma-ring-reset-quirk: %s\n",
+		 ringacc->dma_ring_reset_quirk ? "enabled" : "disabled");
+
+	return ringacc;
+}
+
 struct ringacc_match_data {
 	struct k3_nav_ringacc_ops ops;
 };
diff --git a/include/linux/soc/ti/k3-navss-ringacc.h b/include/linux/soc/ti/k3-navss-ringacc.h
index 9176277ff0..1e9b7d9125 100644
--- a/include/linux/soc/ti/k3-navss-ringacc.h
+++ b/include/linux/soc/ti/k3-navss-ringacc.h
@@ -238,4 +238,19 @@  int k3_nav_ringacc_ring_push_head(struct k3_nav_ring *ring, void *elem);
  */
 int k3_nav_ringacc_ring_pop_tail(struct k3_nav_ring *ring, void *elem);
 
+/* DMA ring support */
+struct ti_sci_handle;
+
+/**
+ * struct struct k3_ringacc_init_data - Initialization data for DMA rings
+ */
+struct k3_ringacc_init_data {
+	const struct ti_sci_handle *tisci;
+	u32 tisci_dev_id;
+	u32 num_rings;
+};
+
+struct k3_nav_ringacc *k3_ringacc_dmarings_init(struct udevice *dev,
+						struct k3_ringacc_init_data *data);
+
 #endif /* __SOC_TI_K3_NAVSS_RINGACC_API_H_ */