* [PATCH 2/4] mtd: hyperbus: hbmc-am654: Fix direct mapping setup flash access
2020-09-17 7:47 [PATCH 0/4] mtd: hyperbus: hbmc-am654: Add DMA support Vignesh Raghavendra
2020-09-17 7:47 ` [PATCH 1/4] mtd: hyperbus: Provide per device private pointer Vignesh Raghavendra
@ 2020-09-17 7:47 ` Vignesh Raghavendra
2020-09-17 7:47 ` [PATCH 3/4] mtd: hyperbus: hbmc-am654: Drop pm_runtime* calls from probe Vignesh Raghavendra
2020-09-17 7:47 ` [PATCH 4/4] mtd: hyperbus: hbmc-am654: Add DMA support for reads Vignesh Raghavendra
3 siblings, 0 replies; 5+ messages in thread
From: Vignesh Raghavendra @ 2020-09-17 7:47 UTC (permalink / raw)
To: Vignesh Raghavendra
Cc: Miquel Raynal, Richard Weinberger, Sergei Shtylyov, linux-mtd,
linux-kernel
Setting up of direct mapping should be done with flash node's IO
address space and not with controller's IO region.
Fixes: b6fe8bc67d2d3 ("mtd: hyperbus: move direct mapping setup to AM654 HBMC driver")
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
drivers/mtd/hyperbus/hbmc-am654.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/hyperbus/hbmc-am654.c b/drivers/mtd/hyperbus/hbmc-am654.c
index e0e33f6bf513..1e70ecfffa39 100644
--- a/drivers/mtd/hyperbus/hbmc-am654.c
+++ b/drivers/mtd/hyperbus/hbmc-am654.c
@@ -70,7 +70,8 @@ static int am654_hbmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- ret = of_address_to_resource(np, 0, &res);
+ priv->hbdev.np = of_get_next_child(np, NULL);
+ ret = of_address_to_resource(priv->hbdev.np, 0, &res);
if (ret)
return ret;
@@ -103,7 +104,6 @@ static int am654_hbmc_probe(struct platform_device *pdev)
priv->ctlr.dev = dev;
priv->ctlr.ops = &am654_hbmc_ops;
priv->hbdev.ctlr = &priv->ctlr;
- priv->hbdev.np = of_get_next_child(dev->of_node, NULL);
ret = hyperbus_register_device(&priv->hbdev);
if (ret) {
dev_err(dev, "failed to register controller\n");
--
2.28.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 3/4] mtd: hyperbus: hbmc-am654: Drop pm_runtime* calls from probe
2020-09-17 7:47 [PATCH 0/4] mtd: hyperbus: hbmc-am654: Add DMA support Vignesh Raghavendra
2020-09-17 7:47 ` [PATCH 1/4] mtd: hyperbus: Provide per device private pointer Vignesh Raghavendra
2020-09-17 7:47 ` [PATCH 2/4] mtd: hyperbus: hbmc-am654: Fix direct mapping setup flash access Vignesh Raghavendra
@ 2020-09-17 7:47 ` Vignesh Raghavendra
2020-09-17 7:47 ` [PATCH 4/4] mtd: hyperbus: hbmc-am654: Add DMA support for reads Vignesh Raghavendra
3 siblings, 0 replies; 5+ messages in thread
From: Vignesh Raghavendra @ 2020-09-17 7:47 UTC (permalink / raw)
To: Vignesh Raghavendra
Cc: Miquel Raynal, Richard Weinberger, Sergei Shtylyov, linux-mtd,
linux-kernel
Recent genpd changes for K3 platform ensure device is ON before driver
probe is called. Therefore, drop redundant pm_runtime_* calls from
driver to simplify the code.
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
drivers/mtd/hyperbus/hbmc-am654.c | 16 ++--------------
1 file changed, 2 insertions(+), 14 deletions(-)
diff --git a/drivers/mtd/hyperbus/hbmc-am654.c b/drivers/mtd/hyperbus/hbmc-am654.c
index 1e70ecfffa39..b6a2400fcaa9 100644
--- a/drivers/mtd/hyperbus/hbmc-am654.c
+++ b/drivers/mtd/hyperbus/hbmc-am654.c
@@ -13,7 +13,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
#include <linux/types.h>
#define AM654_HBMC_CALIB_COUNT 25
@@ -89,13 +88,6 @@ static int am654_hbmc_probe(struct platform_device *pdev)
priv->mux_ctrl = control;
}
- pm_runtime_enable(dev);
- ret = pm_runtime_get_sync(dev);
- if (ret < 0) {
- pm_runtime_put_noidle(dev);
- goto disable_pm;
- }
-
priv->hbdev.map.size = resource_size(&res);
priv->hbdev.map.virt = devm_ioremap_resource(dev, &res);
if (IS_ERR(priv->hbdev.map.virt))
@@ -107,13 +99,11 @@ static int am654_hbmc_probe(struct platform_device *pdev)
ret = hyperbus_register_device(&priv->hbdev);
if (ret) {
dev_err(dev, "failed to register controller\n");
- pm_runtime_put_sync(&pdev->dev);
- goto disable_pm;
+ goto disable_mux;
}
return 0;
-disable_pm:
- pm_runtime_disable(dev);
+disable_mux:
if (priv->mux_ctrl)
mux_control_deselect(priv->mux_ctrl);
return ret;
@@ -127,8 +117,6 @@ static int am654_hbmc_remove(struct platform_device *pdev)
ret = hyperbus_unregister_device(&priv->hbdev);
if (priv->mux_ctrl)
mux_control_deselect(priv->mux_ctrl);
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
return ret;
}
--
2.28.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 4/4] mtd: hyperbus: hbmc-am654: Add DMA support for reads
2020-09-17 7:47 [PATCH 0/4] mtd: hyperbus: hbmc-am654: Add DMA support Vignesh Raghavendra
` (2 preceding siblings ...)
2020-09-17 7:47 ` [PATCH 3/4] mtd: hyperbus: hbmc-am654: Drop pm_runtime* calls from probe Vignesh Raghavendra
@ 2020-09-17 7:47 ` Vignesh Raghavendra
3 siblings, 0 replies; 5+ messages in thread
From: Vignesh Raghavendra @ 2020-09-17 7:47 UTC (permalink / raw)
To: Vignesh Raghavendra
Cc: Miquel Raynal, Richard Weinberger, Sergei Shtylyov, linux-mtd,
linux-kernel
AM654 HyperBus controller provides MMIO interface to read data from
flash. So add DMA memcpy support for reading data over MMIO interface.
This provides 5x improvement in throughput and reduces CPU usage as
well.
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
drivers/mtd/hyperbus/hbmc-am654.c | 130 +++++++++++++++++++++++++++++-
1 file changed, 129 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/hyperbus/hbmc-am654.c b/drivers/mtd/hyperbus/hbmc-am654.c
index b6a2400fcaa9..b6ab55560089 100644
--- a/drivers/mtd/hyperbus/hbmc-am654.c
+++ b/drivers/mtd/hyperbus/hbmc-am654.c
@@ -3,6 +3,10 @@
// Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/
// Author: Vignesh Raghavendra <vigneshr@ti.com>
+#include <linux/completion.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -13,10 +17,18 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
+#include <linux/sched/task_stack.h>
#include <linux/types.h>
#define AM654_HBMC_CALIB_COUNT 25
+struct am654_hbmc_device_priv {
+ struct completion rx_dma_complete;
+ phys_addr_t device_base;
+ struct hyperbus_ctlr *ctlr;
+ struct dma_chan *rx_chan;
+};
+
struct am654_hbmc_priv {
struct hyperbus_ctlr ctlr;
struct hyperbus_device hbdev;
@@ -51,13 +63,107 @@ static int am654_hbmc_calibrate(struct hyperbus_device *hbdev)
return ret;
}
+static void am654_hbmc_dma_callback(void *param)
+{
+ struct am654_hbmc_device_priv *priv = param;
+
+ complete(&priv->rx_dma_complete);
+}
+
+static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to,
+ unsigned long from, ssize_t len)
+
+{
+ enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ struct dma_chan *rx_chan = priv->rx_chan;
+ struct dma_async_tx_descriptor *tx;
+ dma_addr_t dma_dst, dma_src;
+ dma_cookie_t cookie;
+ int ret;
+
+ if (!priv->rx_chan || !virt_addr_valid(to) || object_is_on_stack(to))
+ return -EINVAL;
+
+ dma_dst = dma_map_single(rx_chan->device->dev, to, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(rx_chan->device->dev, dma_dst)) {
+ dev_dbg(priv->ctlr->dev, "DMA mapping failed\n");
+ return -EIO;
+ }
+
+ dma_src = priv->device_base + from;
+ tx = dmaengine_prep_dma_memcpy(rx_chan, dma_dst, dma_src, len, flags);
+ if (!tx) {
+ dev_err(priv->ctlr->dev, "device_prep_dma_memcpy error\n");
+ ret = -EIO;
+ goto unmap_dma;
+ }
+
+ reinit_completion(&priv->rx_dma_complete);
+ tx->callback = am654_hbmc_dma_callback;
+ tx->callback_param = priv;
+ cookie = tx->tx_submit(tx);
+
+ ret = dma_submit_error(cookie);
+ if (ret) {
+ dev_err(priv->ctlr->dev, "dma_submit_error %d\n", cookie);
+ ret = -EIO;
+ goto unmap_dma;
+ }
+
+ dma_async_issue_pending(rx_chan);
+ ret = wait_for_completion_timeout(&priv->rx_dma_complete,
+ msecs_to_jiffies(len + 1000));
+ if (ret <= 0) {
+ dmaengine_terminate_sync(rx_chan);
+ dev_err(priv->ctlr->dev, "DMA wait_for_completion_timeout\n");
+ ret = -ETIMEDOUT;
+ goto unmap_dma;
+ }
+ ret = 0;
+
+unmap_dma:
+ dma_unmap_single(rx_chan->device->dev, dma_dst, len, DMA_FROM_DEVICE);
+ return ret;
+}
+
+static void am654_hbmc_read(struct hyperbus_device *hbdev, void *to,
+ unsigned long from, ssize_t len)
+{
+ struct am654_hbmc_device_priv *priv = hbdev->priv;
+
+ if (len < SZ_1K || am654_hbmc_dma_read(priv, to, from, len))
+ memcpy_fromio(to, hbdev->map.virt + from, len);
+}
+
static const struct hyperbus_ops am654_hbmc_ops = {
.calibrate = am654_hbmc_calibrate,
+ .copy_from = am654_hbmc_read,
};
+static int am654_hbmc_request_mmap_dma(struct am654_hbmc_device_priv *priv)
+{
+ struct dma_chan *rx_chan;
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+
+ rx_chan = dma_request_chan_by_mask(&mask);
+ if (IS_ERR(rx_chan)) {
+ if (PTR_ERR(rx_chan) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ return 0;
+ }
+ priv->rx_chan = rx_chan;
+ init_completion(&priv->rx_dma_complete);
+
+ return 0;
+}
+
static int am654_hbmc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
+ struct am654_hbmc_device_priv *dev_priv;
struct device *dev = &pdev->dev;
struct am654_hbmc_priv *priv;
struct resource res;
@@ -96,13 +202,31 @@ static int am654_hbmc_probe(struct platform_device *pdev)
priv->ctlr.dev = dev;
priv->ctlr.ops = &am654_hbmc_ops;
priv->hbdev.ctlr = &priv->ctlr;
+
+ dev_priv = devm_kzalloc(dev, sizeof(*dev_priv), GFP_KERNEL);
+ if (!dev_priv) {
+ ret = -ENOMEM;
+ goto disable_mux;
+ }
+
+ priv->hbdev.priv = dev_priv;
+ dev_priv->device_base = res.start;
+ dev_priv->ctlr = &priv->ctlr;
+
+ ret = am654_hbmc_request_mmap_dma(dev_priv);
+ if (ret == -EPROBE_DEFER)
+ goto disable_mux;
+
ret = hyperbus_register_device(&priv->hbdev);
if (ret) {
dev_err(dev, "failed to register controller\n");
- goto disable_mux;
+ goto release_dma;
}
return 0;
+release_dma:
+ if (dev_priv->rx_chan)
+ dma_release_channel(dev_priv->rx_chan);
disable_mux:
if (priv->mux_ctrl)
mux_control_deselect(priv->mux_ctrl);
@@ -112,12 +236,16 @@ static int am654_hbmc_probe(struct platform_device *pdev)
static int am654_hbmc_remove(struct platform_device *pdev)
{
struct am654_hbmc_priv *priv = platform_get_drvdata(pdev);
+ struct am654_hbmc_device_priv *dev_priv = priv->hbdev.priv;
int ret;
ret = hyperbus_unregister_device(&priv->hbdev);
if (priv->mux_ctrl)
mux_control_deselect(priv->mux_ctrl);
+ if (dev_priv->rx_chan)
+ dma_release_channel(dev_priv->rx_chan);
+
return ret;
}
--
2.28.0
^ permalink raw reply [flat|nested] 5+ messages in thread