aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/fpga/zynqmp-fpga.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/fpga/zynqmp-fpga.c')
-rw-r--r--drivers/fpga/zynqmp-fpga.c260
1 files changed, 241 insertions, 19 deletions
diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
index b8a88d21d038..894cc36349fd 100644
--- a/drivers/fpga/zynqmp-fpga.c
+++ b/drivers/fpga/zynqmp-fpga.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2019 Xilinx, Inc.
+ * Copyright (C) 2016 - 2019 Xilinx, Inc.
*/
+#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/io.h>
@@ -10,19 +11,73 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/string.h>
+#include <linux/seq_file.h>
#include <linux/firmware/xlnx-zynqmp.h>
/* Constant Definitions */
#define IXR_FPGA_DONE_MASK BIT(3)
+#define IXR_FPGA_ENCRYPTION_EN 0x00000008U
+
+#define READ_DMA_SIZE 0x200
+#define DUMMY_FRAMES_SIZE 0x64
+#define PCAP_READ_CLKFREQ 25000000
+
+static bool readback_type;
+module_param(readback_type, bool, 0644);
+MODULE_PARM_DESC(readback_type,
+ "readback_type 0-configuration register read "
+ "1- configuration data read (default: 0)");
+
+static const struct zynqmp_eemi_ops *eemi_ops;
+
+/**
+ * struct zynqmp_configreg - Configuration register offsets
+ * @reg: Name of the configuration register.
+ * @offset: Register offset.
+ */
+struct zynqmp_configreg {
+ char *reg;
+ u32 offset;
+};
+
+static struct zynqmp_configreg cfgreg[] = {
+ {.reg = "CRC", .offset = 0},
+ {.reg = "FAR", .offset = 1},
+ {.reg = "FDRI", .offset = 2},
+ {.reg = "FDRO", .offset = 3},
+ {.reg = "CMD", .offset = 4},
+ {.reg = "CTRL0", .offset = 5},
+ {.reg = "MASK", .offset = 6},
+ {.reg = "STAT", .offset = 7},
+ {.reg = "LOUT", .offset = 8},
+ {.reg = "COR0", .offset = 9},
+ {.reg = "MFWR", .offset = 10},
+ {.reg = "CBC", .offset = 11},
+ {.reg = "IDCODE", .offset = 12},
+ {.reg = "AXSS", .offset = 13},
+ {.reg = "COR1", .offset = 14},
+ {.reg = "WBSTR", .offset = 16},
+ {.reg = "TIMER", .offset = 17},
+ {.reg = "BOOTSTS", .offset = 22},
+ {.reg = "CTRL1", .offset = 24},
+ {}
+};
/**
* struct zynqmp_fpga_priv - Private data structure
* @dev: Device data structure
+ * @lock: Mutex lock for device
+ * @clk: Clock resource for pcap controller
* @flags: flags which is used to identify the bitfile type
+ * @size: Size of the Bitstream used for readback
*/
struct zynqmp_fpga_priv {
struct device *dev;
+ struct mutex lock;
+ struct clk *clk;
+ char *key;
u32 flags;
+ u32 size;
};
static int zynqmp_fpga_ops_write_init(struct fpga_manager *mgr,
@@ -33,40 +88,62 @@ static int zynqmp_fpga_ops_write_init(struct fpga_manager *mgr,
priv = mgr->priv;
priv->flags = info->flags;
+ priv->key = info->key;
return 0;
}
static int zynqmp_fpga_ops_write(struct fpga_manager *mgr,
- const char *buf, size_t size)
+ const char *buf, size_t size)
{
- const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
struct zynqmp_fpga_priv *priv;
- dma_addr_t dma_addr;
- u32 eemi_flags = 0;
char *kbuf;
+ size_t dma_size;
+ dma_addr_t dma_addr;
int ret;
- if (IS_ERR_OR_NULL(eemi_ops) || !eemi_ops->fpga_load)
+ if (!eemi_ops->fpga_load)
return -ENXIO;
priv = mgr->priv;
+ priv->size = size;
- kbuf = dma_alloc_coherent(priv->dev, size, &dma_addr, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
+ if (!mutex_trylock(&priv->lock))
+ return -EBUSY;
- memcpy(kbuf, buf, size);
+ ret = clk_enable(priv->clk);
+ if (ret)
+ goto err_unlock;
- wmb(); /* ensure all writes are done before initiate FW call */
+ if (priv->flags & IXR_FPGA_ENCRYPTION_EN)
+ dma_size = size + ENCRYPTED_KEY_LEN;
+ else
+ dma_size = size;
- if (priv->flags & FPGA_MGR_PARTIAL_RECONFIG)
- eemi_flags |= XILINX_ZYNQMP_PM_FPGA_PARTIAL;
+ kbuf = dma_alloc_coherent(priv->dev, dma_size, &dma_addr, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto disable_clk;
+ }
- ret = eemi_ops->fpga_load(dma_addr, size, eemi_flags);
+ memcpy(kbuf, buf, size);
+
+ if (priv->flags & IXR_FPGA_ENCRYPTION_EN)
+ memcpy(kbuf + size, priv->key, ENCRYPTED_KEY_LEN);
- dma_free_coherent(priv->dev, size, kbuf, dma_addr);
+ wmb(); /* ensure all writes are done before initiate FW call */
+ if (priv->flags & IXR_FPGA_ENCRYPTION_EN)
+ ret = eemi_ops->fpga_load(dma_addr, dma_addr + size,
+ priv->flags);
+ else
+ ret = eemi_ops->fpga_load(dma_addr, size, priv->flags);
+
+ dma_free_coherent(priv->dev, dma_size, kbuf, dma_addr);
+disable_clk:
+ clk_disable(priv->clk);
+err_unlock:
+ mutex_unlock(&priv->lock);
return ret;
}
@@ -78,10 +155,9 @@ static int zynqmp_fpga_ops_write_complete(struct fpga_manager *mgr,
static enum fpga_mgr_states zynqmp_fpga_ops_state(struct fpga_manager *mgr)
{
- const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
u32 status;
- if (IS_ERR_OR_NULL(eemi_ops) || !eemi_ops->fpga_get_status)
+ if (!eemi_ops->fpga_get_status)
return FPGA_MGR_STATE_UNKNOWN;
eemi_ops->fpga_get_status(&status);
@@ -91,25 +167,152 @@ static enum fpga_mgr_states zynqmp_fpga_ops_state(struct fpga_manager *mgr)
return FPGA_MGR_STATE_UNKNOWN;
}
+static int zynqmp_fpga_read_cfgreg(struct fpga_manager *mgr,
+ struct seq_file *s)
+{
+ struct zynqmp_fpga_priv *priv = mgr->priv;
+ int ret, val;
+ unsigned int *buf;
+ dma_addr_t dma_addr;
+ struct zynqmp_configreg *p = cfgreg;
+
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ buf = dma_alloc_coherent(mgr->dev.parent, READ_DMA_SIZE,
+ &dma_addr, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto disable_clk;
+ }
+
+ seq_puts(s, "zynqMP FPGA Configuration register contents are\n");
+
+ while (p->reg) {
+ ret = eemi_ops->fpga_read(p->offset, dma_addr, readback_type,
+ &val);
+ if (ret)
+ goto free_dmabuf;
+ seq_printf(s, "%s --> \t %x \t\r\n", p->reg, val);
+ p++;
+ }
+
+free_dmabuf:
+ dma_free_coherent(mgr->dev.parent, READ_DMA_SIZE, buf,
+ dma_addr);
+disable_clk:
+ clk_disable(priv->clk);
+
+ return ret;
+}
+
+static int zynqmp_fpga_read_cfgdata(struct fpga_manager *mgr,
+ struct seq_file *s)
+{
+ struct zynqmp_fpga_priv *priv;
+ int ret, data_offset;
+ unsigned int *buf;
+ dma_addr_t dma_addr;
+ size_t size;
+ int clk_rate;
+
+ priv = mgr->priv;
+ size = priv->size + READ_DMA_SIZE + DUMMY_FRAMES_SIZE;
+
+ /*
+ * There is no h/w flow control for pcap read
+ * to prevent the FIFO from over flowing, reduce
+ * the PCAP operating frequency.
+ */
+ clk_rate = clk_get_rate(priv->clk);
+ clk_unprepare(priv->clk);
+ ret = clk_set_rate(priv->clk, PCAP_READ_CLKFREQ);
+ if (ret) {
+ dev_err(&mgr->dev, "Unable to reduce the PCAP freq %d\n", ret);
+ goto prepare_clk;
+ }
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(&mgr->dev, "Cannot enable clock.\n");
+ goto restore_pcap_clk;
+ }
+
+ buf = dma_alloc_coherent(mgr->dev.parent, size, &dma_addr,
+ GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto disable_clk;
+ }
+
+ seq_puts(s, "zynqMP FPGA Configuration data contents are\n");
+ ret = eemi_ops->fpga_read((priv->size + DUMMY_FRAMES_SIZE) / 4,
+ dma_addr, readback_type, &data_offset);
+ if (ret)
+ goto free_dmabuf;
+
+ seq_write(s, &buf[data_offset], priv->size);
+
+free_dmabuf:
+ dma_free_coherent(mgr->dev.parent, size, buf, dma_addr);
+disable_clk:
+ clk_disable_unprepare(priv->clk);
+restore_pcap_clk:
+ clk_set_rate(priv->clk, clk_rate);
+prepare_clk:
+ clk_prepare(priv->clk);
+
+ return ret;
+}
+
+static int zynqmp_fpga_ops_read(struct fpga_manager *mgr, struct seq_file *s)
+{
+ struct zynqmp_fpga_priv *priv = mgr->priv;
+ int ret;
+
+ if (!eemi_ops->fpga_read)
+ return -ENXIO;
+
+ if (!mutex_trylock(&priv->lock))
+ return -EBUSY;
+
+ if (readback_type)
+ ret = zynqmp_fpga_read_cfgdata(mgr, s);
+ else
+ ret = zynqmp_fpga_read_cfgreg(mgr, s);
+
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
static const struct fpga_manager_ops zynqmp_fpga_ops = {
.state = zynqmp_fpga_ops_state,
.write_init = zynqmp_fpga_ops_write_init,
.write = zynqmp_fpga_ops_write,
.write_complete = zynqmp_fpga_ops_write_complete,
+ .read = zynqmp_fpga_ops_read,
};
static int zynqmp_fpga_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct zynqmp_fpga_priv *priv;
- struct fpga_manager *mgr;
int ret;
+ struct fpga_manager *mgr;
+
+ eemi_ops = zynqmp_pm_get_eemi_ops();
+ if (IS_ERR(eemi_ops))
+ return PTR_ERR(eemi_ops);
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
+ mutex_init(&priv->lock);
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(44));
+ if (ret < 0)
+ dev_err(dev, "no usable DMA configuration");
mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
&zynqmp_fpga_ops, priv);
@@ -118,9 +321,23 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mgr);
+ priv->clk = devm_clk_get(dev, "ref_clk");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(dev, "failed to to get pcp ref_clk (%d)\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare(priv->clk);
+ if (ret) {
+ dev_err(dev, "Cannot enable clock.\n");
+ return ret;
+ }
+
ret = fpga_mgr_register(mgr);
if (ret) {
dev_err(dev, "unable to register FPGA manager");
+ clk_unprepare(priv->clk);
return ret;
}
@@ -129,9 +346,14 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
static int zynqmp_fpga_remove(struct platform_device *pdev)
{
- struct fpga_manager *mgr = platform_get_drvdata(pdev);
+ struct zynqmp_fpga_priv *priv;
+ struct fpga_manager *mgr;
+
+ mgr = platform_get_drvdata(pdev);
+ priv = mgr->priv;
fpga_mgr_unregister(mgr);
+ clk_unprepare(priv->clk);
return 0;
}