@@ -522,4 +522,15 @@ config ST_FDMA
Say Y here if you have such a chipset.
If unsure, say N.
+config ST_FDMA_XBAR
+ bool "ST FDMA crossbar"
+ depends on ST_FDMA
+ default y
+ help
+ Enable support for ST FDMA crossbar.
+ xbar add flexibility and increase the number of peripheral request
+ can be used by fdma xbar can multiplex until 96 peripheral requests
+ to one of 3 fdma controller
+
+
endif
@@ -50,6 +50,7 @@ obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
obj-$(CONFIG_FSL_RAID) += fsl_raid.o
obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
+obj-$(CONFIG_ST_FDMA_XBAR) += st_fdma_xbar.o
obj-$(CONFIG_ST_FDMA) += st_fdma.o
obj-y += xilinx/
obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o
@@ -336,7 +336,9 @@ static int st_fdma_dreq_get(struct st_fdma_chan *fchan)
return -EINVAL;
}
- if (try || req_line_cfg >= ST_FDMA_NR_DREQS) {
+ if (fdev->xbar_dev)
+ dreq_line = ffz(fdev->dreq_mask);
+ else if (try || req_line_cfg >= ST_FDMA_NR_DREQS) {
dev_err(fdev->dev, "Invalid or used req line\n");
return -EINVAL;
} else {
@@ -903,6 +905,15 @@ static int st_fdma_slave_config(struct dma_chan *chan,
else
return -EINVAL;
+ if (fdev->xbar_dev) {
+ if (st_fdma_xbar_mux(fdev->xbar_dev, fchan->cfg.req_line,
+ fchan->dreq_line)) {
+ dev_err(fdev->dev, "Error routing req line\n");
+ clear_bit(fchan->dreq_line, &fdev->dreq_mask);
+ return -EINVAL;
+ }
+ }
+
fchan->cfg.req_ctrl |= REQ_CTRL_NUM_OPS(maxburst-1);
dreq_write(fchan, fchan->cfg.req_ctrl, REQ_CTRL);
@@ -1044,6 +1055,13 @@ static int st_fdma_probe(struct platform_device *pdev)
if (ret)
goto err_clk;
+ fdev->xbar_dev = st_fdma_xbar_request(&pdev->dev);
+ if (IS_ERR(fdev->xbar_dev)) {
+ dev_err(&pdev->dev, "Failed to request xbar:%ld\n",
+ PTR_ERR(fdev->xbar_dev));
+ goto err_clk;
+ }
+
/* Initialise the FDMA dreq (reserve 0 & 31 for FDMA use) */
fdev->dreq_mask = BIT(0) | BIT(31);
@@ -1081,7 +1099,10 @@ static int st_fdma_probe(struct platform_device *pdev)
goto err_dma_dev;
}
- dev_info(&pdev->dev, "ST FDMA engine driver, irq:%d\n", irq);
+ dev_info(&pdev->dev, "ST FDMA engine driver, irq:%d xbar(%s, id:%d)\n",
+ irq, fdev->xbar_dev ?
+ dev_name(&fdev->xbar_dev->pdev->dev) : "no",
+ fdev->xbar_dev ? fdev->xbar_dev->fdma_id : 0);
return 0;
@@ -17,6 +17,20 @@
#define ST_FDMA_NR_DREQS 32
#define EM_SLIM 102 /* No official SLIM ELF ID */
+struct st_fdma_xbar {
+ void __iomem *io_base;
+ struct clk *clk;
+ u32 nr_requests;
+ u32 max_nr_requests;
+};
+
+struct st_fdma_xbar_dev {
+ struct platform_device *pdev;
+ struct st_fdma_xbar *xbar;
+ struct device *fdev;
+ u32 fdma_id;
+};
+
enum {
CLK_SLIM,
CLK_HI,
@@ -135,6 +149,7 @@ struct st_fdma_dev {
struct st_fdma_chan *chans;
+ struct st_fdma_xbar_dev *xbar_dev;
spinlock_t dreq_lock;
unsigned long dreq_mask;
@@ -142,6 +157,23 @@ struct st_fdma_dev {
atomic_t fw_loaded;
};
+#if defined(CONFIG_ST_FDMA_XBAR)
+struct st_fdma_xbar_dev *st_fdma_xbar_request(struct device *device);
+int st_fdma_xbar_mux(struct st_fdma_xbar_dev *xbar_dev,
+ u32 in_dreq, u32 fdma_dreq);
+#else
+struct st_fdma_xbar_dev *st_fdma_xbar_request(struct device *device)
+{
+ return NULL;
+}
+
+int st_fdma_xbar_mux(struct st_fdma_xbar_dev *xbar_dev,
+ u32 in_dreq, u32 fdma_dreq)
+{
+ return -ENODEV;
+}
+#endif
+
/* Registers*/
/* FDMA interface */
#define FDMA_ID_OFST 0x00000
new file mode 100644
@@ -0,0 +1,149 @@
+/*
+ * st_fdma_xbar.c
+ *
+ * Copyright (C) 2014 STMicroelectronics
+ * Author: Ludovic Barre <Ludovic.barre@st.com>
+ * License terms: GNU General Public License (GPL), version 2
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "st_fdma.h"
+
+#define XBAR_1_0_MAX_REQ 96
+
+int st_fdma_xbar_mux(struct st_fdma_xbar_dev *xbar_dev,
+ u32 per_req, u32 fdma_req)
+{
+ u32 out_req;
+
+ if (per_req >= xbar_dev->xbar->nr_requests)
+ return -EINVAL;
+
+ out_req = (xbar_dev->fdma_id * ST_FDMA_NR_DREQS) + fdma_req;
+ writel(per_req, xbar_dev->xbar->io_base + (out_req << 2));
+
+ dev_dbg(&xbar_dev->pdev->dev, "in_req:%d out_req:%d\n",
+ per_req, out_req);
+
+ return 0;
+}
+
+struct st_fdma_xbar_dev *st_fdma_xbar_request(struct device *device)
+{
+ struct device_node *np = device->of_node;
+ struct st_fdma_xbar_dev *xbar_dev;
+ struct of_phandle_args args;
+ int err;
+
+ err = of_parse_phandle_with_args(np, "st,fdma-xbar",
+ "#st,fdma-xbar-cells", 0, &args);
+ if (err)
+ return NULL;
+
+ xbar_dev = kzalloc(sizeof(*xbar_dev), GFP_KERNEL);
+ if (!xbar_dev) {
+ of_node_put(args.np);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ xbar_dev->pdev = of_find_device_by_node(args.np);
+ if (!xbar_dev->pdev) {
+ of_node_put(args.np);
+ err = -ENODEV;
+ goto free;
+ }
+
+ of_node_put(args.np);
+
+ xbar_dev->xbar = platform_get_drvdata(xbar_dev->pdev);
+ if (!xbar_dev->xbar) {
+ err = -EPROBE_DEFER;
+ goto pdev_put;
+ }
+
+ xbar_dev->fdma_id = args.args[0];
+ xbar_dev->fdev = device;
+
+ return xbar_dev;
+
+pdev_put:
+ platform_device_put(xbar_dev->pdev);
+free:
+ kfree(xbar_dev);
+out:
+ return ERR_PTR(err);
+}
+
+void st_fdma_xbar_free(struct st_fdma_xbar_dev *device)
+{
+ platform_device_put(device->pdev);
+ kfree(device);
+}
+
+static const struct of_device_id st_fdma_xbar_match[] = {
+ { .compatible = "st,fdma-xbar-1.0", .data = (void *)XBAR_1_0_MAX_REQ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, st_fdma_xbar_match);
+
+static int st_fdma_xbar_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *np = pdev->dev.of_node;
+ struct st_fdma_xbar *xbar;
+ struct resource *res;
+
+ match = of_match_device((st_fdma_xbar_match), &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "No device match found\n");
+ return -ENODEV;
+ }
+
+ xbar = devm_kzalloc(&pdev->dev, sizeof(*xbar), GFP_KERNEL);
+ if (!xbar)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xbar->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(xbar->io_base))
+ return PTR_ERR(xbar->io_base);
+
+ xbar->max_nr_requests = (unsigned long)match->data;
+
+ if (of_property_read_u32(np, "dma-requests", &xbar->nr_requests))
+ xbar->nr_requests = xbar->max_nr_requests;
+
+ if (xbar->nr_requests > xbar->max_nr_requests) {
+ dev_err(&pdev->dev, "Nr requests not supported\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, xbar);
+
+ return 0;
+}
+
+static int st_fdma_xbar_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver st_fdma_xbar_driver = {
+ .driver = {
+ .name = "st-fdma-xbar",
+ .of_match_table = st_fdma_xbar_match,
+ },
+ .probe = st_fdma_xbar_probe,
+ .remove = st_fdma_xbar_remove,
+};
+module_platform_driver(st_fdma_xbar_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMicroelectronics FDMA cross bar");
+MODULE_AUTHOR("Ludovic.barre <Ludovic.barre@st.com>");