Re: [PATCH v2 02/14] soc: ti: k3: add navss ringacc driver
From: Grygorii Strashko <grygorii.strashko@ti.com>
Date: 2019-09-09 16:59:20
Also in:
dmaengine, linux-devicetree, lkml
On 09/09/2019 16:00, Peter Ujfalusi wrote:
Hi, Grygorii, can you take a look? On 09/09/2019 9.09, Tero Kristo wrote:quoted
Hi, Mostly some cosmetic comments below, other than that seems fine to me. On 30/07/2019 12:34, Peter Ujfalusi wrote:quoted
From: Grygorii Strashko <grygorii.strashko@ti.com> The Ring Accelerator (RINGACC or RA) provides hardware acceleration to enable straightforward passing of work between a producer and a consumer. There is one RINGACC module per NAVSS on TI AM65x SoCs. The RINGACC converts constant-address read and write accesses to equivalent read or write accesses to a circular data structure in memory. The RINGACC eliminates the need for each DMA controller which needs to access ring elements from having to know the current state of the ring (base address, current offset). The DMA controller performs a read or write access to a specific address range (which maps to the source interface on the RINGACC) and the RINGACC replaces the address for the transaction with a new address which corresponds to the head or tail element of the ring (head for reads, tail for writes). Since the RINGACC maintains the state, multiple DMA controllers or channels are allowed to coherently share the same rings as applicable. The RINGACC is able to place data which is destined towards software into cached memory directly. Supported ring modes: - Ring Mode - Messaging Mode - Credentials Mode - Queue Manager Mode TI-SCI integration: Texas Instrument's System Control Interface (TI-SCI) Message Protocol now has control over Ringacc module resources management (RM) and Rings configuration. The corresponding support of TI-SCI Ringacc module RM protocol introduced as option through DT parameters: - ti,sci: phandle on TI-SCI firmware controller DT node - ti,sci-dev-id: TI-SCI device identifier as per TI-SCI firmware spec if both parameters present - Ringacc driver will configure/free/reset Rings using TI-SCI Message Ringacc RM Protocol. The Ringacc driver manages Rings allocation by itself now and requests TI-SCI firmware to allocate and configure specific Rings only. It's done this way because, Linux driver implements two stage Rings allocation and configuration (allocate ring and configure ring) while I-SCI MessageI-SCI should be TI-SCI I believe.Yes, it supposed to be.quoted
quoted
Protocol supports only one combined operation (allocate+configure). Grygorii Strashko [off-list ref]Above seems to be missing SoB?Oh, it is really missing.quoted
quoted
Signed-off-by: Peter Ujfalusi <redacted> --- drivers/soc/ti/Kconfig | 17 + drivers/soc/ti/Makefile | 1 + drivers/soc/ti/k3-ringacc.c | 1191 +++++++++++++++++++++++++++++ include/linux/soc/ti/k3-ringacc.h | 262 +++++++ 4 files changed, 1471 insertions(+) create mode 100644 drivers/soc/ti/k3-ringacc.c create mode 100644 include/linux/soc/ti/k3-ringacc.hdiff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index cf545f428d03..10c76faa503e 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig@@ -80,6 +80,23 @@ config TI_SCI_PM_DOMAINS called ti_sci_pm_domains. Note this is needed early in bootbefore rootfs may be available. +config TI_K3_RINGACC + tristate "K3 Ring accelerator Sub System" + depends on ARCH_K3 || COMPILE_TEST + depends on TI_SCI_INTA_IRQCHIP + default y + help + Say y here to support the K3 Ring accelerator module. + The Ring Accelerator (RINGACC or RA) provides hardware acceleration + to enable straightforward passing of work between a producer + and a consumer. There is one RINGACC module per NAVSS on TI AM65x SoCs + If unsure, say N. + +config TI_K3_RINGACC_DEBUG + tristate "K3 Ring accelerator Sub System tests and debug" + depends on TI_K3_RINGACC + default n + endif # SOC_TI config TI_SCI_INTA_MSI_DOMAINdiff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile index b3868d392d4f..cc4bc8b08bf5 100644 --- a/drivers/soc/ti/Makefile +++ b/drivers/soc/ti/Makefile@@ -9,3 +9,4 @@ obj-$(CONFIG_AMX3_PM) += pm33xx.o obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o +obj-$(CONFIG_TI_K3_RINGACC) += k3-ringacc.odiff --git a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c new file mode 100644 index 000000000000..401dfc963319 --- /dev/null +++ b/drivers/soc/ti/k3-ringacc.c@@ -0,0 +1,1191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI K3 NAVSS Ring Accelerator subsystem driver + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + */ + +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/soc/ti/k3-ringacc.h> +#include <linux/soc/ti/ti_sci_protocol.h> +#include <linux/soc/ti/ti_sci_inta_msi.h> +#include <linux/of_irq.h> +#include <linux/irqdomain.h> + +static LIST_HEAD(k3_ringacc_list); +static DEFINE_MUTEX(k3_ringacc_list_lock); + +#ifdef CONFIG_TI_K3_RINGACC_DEBUG +#define k3_nav_dbg(dev, arg...) dev_err(dev, arg)dev_err seems exaggeration for debug purposes, maybe just dev_info.quoted
+static void dbg_writel(u32 v, void __iomem *reg) +{ + pr_err("WRITEL(32): v(%08X)-->reg(%p)\n", v, reg);Again, maybe just pr_info.I think I'll just drop CONFIG_TI_K3_RINGACC_DEBUG altogether along with dbg_writel/dbg_readl/k3_nav_dbg and use dev_dbg() when appropriate.
Sounds good.
quoted
quoted
+ writel(v, reg); +} + +static u32 dbg_readl(void __iomem *reg) +{ + u32 v; + + v = readl(reg); + pr_err("READL(32): v(%08X)<--reg(%p)\n", v, reg); + return v; +} +#else +#define k3_nav_dbg(dev, arg...) dev_dbg(dev, arg) +#define dbg_writel(v, reg) writel(v, reg)Do you need to use hard writel, writel_relaxed is not enough?not sure if we really need the barriers, but __raw_writel() should be fine here imho
xxx_relaxed relaxed versions should be used only when necessary and with adding appropriate comments why they've been used and what benefits from using them for each particular case. So, i do not agree with this blind conversation.
quoted
quoted
+ +#define dbg_readl(reg) readl(reg)Same as above but for read?__raw_readl() could be fine in also.
No. __raw_xxx api should never be used by drivers.
...quoted
quoted
+/** + * struct k3_ringacc - Rings accelerator descriptor + * + * @dev - pointer on RA device + * @proxy_gcfg - RA proxy global config registers + * @proxy_target_base - RA proxy datapath region + * @num_rings - number of ring in RA + * @rm_gp_range - general purpose rings range from tisci + * @dma_ring_reset_quirk - DMA reset w/a enable + * @num_proxies - number of RA proxies + * @rings - array of rings descriptors (struct @k3_ring) + * @list - list of RAs in the system + * @tisci - pointer ti-sci handle + * @tisci_ring_ops - ti-sci rings ops + * @tisci_dev_id - ti-sci device id + */
...
quoted
quoted
+ +#ifdef CONFIG_TI_K3_RINGACC_DEBUG +void k3_ringacc_ring_dump(struct k3_ring *ring) +{ + struct device *dev = ring->parent->dev; + + k3_nav_dbg(dev, "dump ring: %d\n", ring->ring_id); + k3_nav_dbg(dev, "dump mem virt %p, dma %pad\n", + ring->ring_mem_virt, &ring->ring_mem_dma); + k3_nav_dbg(dev, "dump elmsize %d, size %d, mode %d, proxy_id %d\n", + ring->elm_size, ring->size, ring->mode, ring->proxy_id); + + k3_nav_dbg(dev, "dump ring_rt_regs: db%08x\n", + readl(&ring->rt->db));Why not use readl_relaxed in this func?__raw_readl() might be enough?
No Raw, but this seems only one place where relaxed version can be used.
quoted
quoted
+ k3_nav_dbg(dev, "dump occ%08x\n", + readl(&ring->rt->occ)); + k3_nav_dbg(dev, "dump indx%08x\n", + readl(&ring->rt->indx)); + k3_nav_dbg(dev, "dump hwocc%08x\n", + readl(&ring->rt->hwocc)); + k3_nav_dbg(dev, "dump hwindx%08x\n", + readl(&ring->rt->hwindx)); + + if (ring->ring_mem_virt) + print_hex_dump(KERN_ERR, "dump ring_mem_virt ", + DUMP_PREFIX_NONE, 16, 1, + ring->ring_mem_virt, 16 * 8, false); +} +EXPORT_SYMBOL_GPL(k3_ringacc_ring_dump);Do you really need to export a debug function?It might come helpful for clients to dump the ring status runtime, but since we don't have users, I'll move it to static.
Yep. It was exported for debug purposes. But hence there are no active users - cna be removed.
quoted
quoted
+#endif + +struct k3_ring *k3_ringacc_request_ring(struct k3_ringacc *ringacc, + int id, u32 flags) +{ + int proxy_id = K3_RINGACC_PROXY_NOT_USED; + + mutex_lock(&ringacc->req_lock); + + if (id == K3_RINGACC_RING_ID_ANY) { + /* Request for any general purpose ring */ + struct ti_sci_resource_desc *gp_rings = + &ringacc->rm_gp_range->desc[0]; + unsigned long size; + + size = gp_rings->start + gp_rings->num; + id = find_next_zero_bit(ringacc->rings_inuse, size, + gp_rings->start); + if (id == size) + goto error; + } else if (id < 0) { + goto error; + } + + if (test_bit(id, ringacc->rings_inuse) && + !(ringacc->rings[id].flags & K3_RING_FLAG_SHARED)) + goto error; + else if (ringacc->rings[id].flags & K3_RING_FLAG_SHARED) + goto out; + + if (flags & K3_RINGACC_RING_USE_PROXY) { + proxy_id = find_next_zero_bit(ringacc->proxy_inuse, + ringacc->num_proxies, 0); + if (proxy_id == ringacc->num_proxies) + goto error; + } + + if (!try_module_get(ringacc->dev->driver->owner)) + goto error; + + if (proxy_id != K3_RINGACC_PROXY_NOT_USED) { + set_bit(proxy_id, ringacc->proxy_inuse); + ringacc->rings[id].proxy_id = proxy_id; + k3_nav_dbg(ringacc->dev, "Giving ring#%d proxy#%d\n", + id, proxy_id); + } else { + k3_nav_dbg(ringacc->dev, "Giving ring#%d\n", id); + } + + set_bit(id, ringacc->rings_inuse); +out: + ringacc->rings[id].use_count++; + mutex_unlock(&ringacc->req_lock); + return &ringacc->rings[id]; + +error: + mutex_unlock(&ringacc->req_lock); + return NULL; +} +EXPORT_SYMBOL_GPL(k3_ringacc_request_ring); + +static void k3_ringacc_ring_reset_sci(struct k3_ring *ring) +{ + struct k3_ringacc *ringacc = ring->parent; + int ret; + + ret = ringacc->tisci_ring_ops->config( + ringacc->tisci, + TI_SCI_MSG_VALUE_RM_RING_COUNT_VALID, + ringacc->tisci_dev_id, + ring->ring_id, + 0, + 0, + ring->size, + 0, + 0, + 0); + if (ret) + dev_err(ringacc->dev, "TISCI reset ring fail (%d) ring_idx %d\n", + ret, ring->ring_id);Return value of sci ops is masked, why not return it and let the caller handle it properly? Same comment for anything similar that follows.Hrm, there is not much a caller can do other than PANIC in case the ring configuration fails. I can probagate the error, but not sure what action can be taken, if any.quoted
quoted
+} + +void k3_ringacc_ring_reset(struct k3_ring *ring) +{ + if (!ring || !(ring->flags & K3_RING_FLAG_BUSY)) + return; + + ring->occ = 0; + ring->free = 0; + ring->rindex = 0; + ring->windex = 0; + + k3_ringacc_ring_reset_sci(ring); +} +EXPORT_SYMBOL_GPL(k3_ringacc_ring_reset); + +static void k3_ringacc_ring_reconfig_qmode_sci(struct k3_ring *ring, + enum k3_ring_mode mode) +{ + struct k3_ringacc *ringacc = ring->parent; + int ret; + + ret = ringacc->tisci_ring_ops->config( + ringacc->tisci, + TI_SCI_MSG_VALUE_RM_RING_MODE_VALID, + ringacc->tisci_dev_id, + ring->ring_id, + 0, + 0, + 0, + mode, + 0, + 0); + if (ret) + dev_err(ringacc->dev, "TISCI reconf qmode fail (%d) ring_idx %d\n", + ret, ring->ring_id); +} + +void k3_ringacc_ring_reset_dma(struct k3_ring *ring, u32 occ) +{ + if (!ring || !(ring->flags & K3_RING_FLAG_BUSY)) + return; + + if (!ring->parent->dma_ring_reset_quirk) + return; + + if (!occ) + occ = dbg_readl(&ring->rt->occ); + + if (occ) { + u32 db_ring_cnt, db_ring_cnt_cur; + + k3_nav_dbg(ring->parent->dev, "%s %u occ: %u\n", __func__, + ring->ring_id, occ); + /* 2. Reset the ring */2? Where is 1?Oh, I'll fix the numbering.
1. is 'Get ring occupancy count" I think you can just drop numbering
quoted
quoted
+ k3_ringacc_ring_reset_sci(ring); + + /* + * 3. Setup the ring in ring/doorbell mode + * (if not already in this mode) + */ + if (ring->mode != K3_RINGACC_RING_MODE_RING) + k3_ringacc_ring_reconfig_qmode_sci( + ring, K3_RINGACC_RING_MODE_RING); + /* + * 4. Ring the doorbell 2**22 – ringOcc times. + * This will wrap the internal UDMAP ring state occupancy + * counter (which is 21-bits wide) to 0. + */ + db_ring_cnt = (1U << 22) - occ; + + while (db_ring_cnt != 0) { + /* + * Ring the doorbell with the maximum count each + * iteration if possible to minimize the total + * of writes + */ + if (db_ring_cnt > K3_RINGACC_MAX_DB_RING_CNT) + db_ring_cnt_cur = K3_RINGACC_MAX_DB_RING_CNT; + else + db_ring_cnt_cur = db_ring_cnt; + + writel(db_ring_cnt_cur, &ring->rt->db); + db_ring_cnt -= db_ring_cnt_cur; + } + + /* 5. Restore the original ring mode (if not ring mode) */ + if (ring->mode != K3_RINGACC_RING_MODE_RING) + k3_ringacc_ring_reconfig_qmode_sci(ring, ring->mode); + } + + /* 2. Reset the ring */
quoted
quoted
+ +u32 k3_ringacc_get_tisci_dev_id(struct k3_ring *ring) +{ + if (!ring) + return -EINVAL; +What if parent is NULL? Can it ever be here?No, parent can not be NULL as the client would not have the ring in the first place.quoted
quoted
+ return ring->parent->tisci_dev_id; +} +EXPORT_SYMBOL_GPL(k3_ringacc_get_tisci_dev_id); + +int k3_ringacc_get_ring_irq_num(struct k3_ring *ring) +{ + int irq_num; + + if (!ring) + return -EINVAL; + + irq_num = ti_sci_inta_msi_get_virq(ring->parent->dev, ring->ring_id); + if (irq_num <= 0) + irq_num = -EINVAL; + return irq_num; +} +EXPORT_SYMBOL_GPL(k3_ringacc_get_ring_irq_num); + +static int k3_ringacc_ring_cfg_sci(struct k3_ring *ring) +{ + struct k3_ringacc *ringacc = ring->parent; + u32 ring_idx; + int ret; + + if (!ringacc->tisci) + return -EINVAL; + + ring_idx = ring->ring_id; + ret = ringacc->tisci_ring_ops->config( + ringacc->tisci, + TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER, + ringacc->tisci_dev_id, + ring_idx, + lower_32_bits(ring->ring_mem_dma), + upper_32_bits(ring->ring_mem_dma), + ring->size, + ring->mode, + ring->elm_size, + 0); + if (ret) + dev_err(ringacc->dev, "TISCI config ring fail (%d) ring_idx %d\n", + ret, ring_idx); + + return ret; +} + +int k3_ringacc_ring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg) +{ + struct k3_ringacc *ringacc = ring->parent; + int ret = 0; + + if (!ring || !cfg) + return -EINVAL; + if (cfg->elm_size > K3_RINGACC_RING_ELSIZE_256 || + cfg->mode > K3_RINGACC_RING_MODE_QM || + cfg->size & ~K3_RINGACC_CFG_RING_SIZE_ELCNT_MASK || + !test_bit(ring->ring_id, ringacc->rings_inuse)) + return -EINVAL; + + if (ring->use_count != 1)Hmm, isn't this a failure actually?Yes, it is: -EBUSY
No. This is for shared rings. 0 - should never happens once ring is requested. 1 - only one user - configure ring
1 - shared ring which is configured already - just exit as ring configure already.
quoted
quoted
+ return 0; + + ring->size = cfg->size; + ring->elm_size = cfg->elm_size; + ring->mode = cfg->mode; + ring->occ = 0; + ring->free = 0; + ring->rindex = 0; + ring->windex = 0; +
[...] -- Best regards, grygorii _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel