All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 2/2] fpga: zynq-fpga: Add support for readback of configuration registers
@ 2018-07-03  8:44 ` Appana Durga Kedareswara rao
  0 siblings, 0 replies; 3+ messages in thread
From: Appana Durga Kedareswara rao @ 2018-07-03  8:44 UTC (permalink / raw)
  To: atull, mdf, michal.simek, navam
  Cc: linux-fpga, linux-arm-kernel, linux-kernel, Appana Durga Kedareswara rao

This patch adds support for Read-back of
configuration registers in zynq.

Signed-off-by: Appana Durga Kedareswara rao <appana.durga.rao@xilinx.com>
---
 drivers/fpga/zynq-fpga.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 254 insertions(+)

diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index 70b15b3..31d39f0 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -25,6 +25,7 @@
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/pm.h>
@@ -127,6 +128,49 @@
 /* Disable global resets */
 #define FPGA_RST_NONE_MASK		0x0
 
+/* Configuration Registers */
+#define TYPE1_CRC_OFFSET	0x0
+#define TYPE1_FAR_OFFSET	0x1
+#define TYPE1_FDRI_OFFSET	0x2
+#define TYPE1_FDRO_OFFSET	0x3
+#define TYPE1_CMD_OFFSET	0x4
+#define TYPE1_CTRL0_OFFSET	0x5
+#define TYPE1_MASK_OFFSET	0x6
+#define TYPE1_STAT_OFFSET	0x7
+#define TYPE1_LOUT_OFFSET	0x8
+#define TYPE1_COR0_OFFSET	0x9
+#define TYPE1_MFWR_OFFSET	0xa
+#define TYPE1_CBC_OFFSET	0xb
+#define TYPE1_IDCODE_OFFSET	0xc
+#define TYPE1_AXSS_OFFSET	0xd
+#define TYPE1_COR1_OFFSET	0xe
+#define TYPE1_WBSTR_OFFSET	0x10
+#define TYPE1_TIMER_OFFSET	0x11
+#define TYPE1_BOOTSTS_OFFSET	0x16
+#define TYPE1_CTRL1_OFFSET	0x18
+#define TYPE1_BSPI_OFFSET	0x1f
+
+/* Masks for Configuration registers */
+#define RCFG_CMD_MASK		0x00000004
+#define START_CMD_MASK		0x00000005
+#define RCRC_CMD_MASK		0x00000007
+#define SHUTDOWN_CMD_MASK	0x0000000B
+#define DESYNC_WORD_MASK	0x0000000D
+#define BUSWIDTH_SYNCWORD_MASK	0x000000BB
+#define NOOP_WORD_MASK		0x20000000
+#define BUSWIDTH_DETECT_MASK	0x11220044
+#define SYNC_WORD_MASK		0xAA995566
+#define DUMMY_WORD_MASK		0xFFFFFFFF
+
+#define TYPE1_HDR_SHIFT		29
+#define TYPE1_REG_SHIFT		13
+#define TYPE1_OP_SHIFT		27
+#define TYPE1_OPCODE_NOOP	0
+#define TYPE1_OPCODE_READ	1
+#define TYPE1_OPCODE_WRITE	2
+
+#define CFGREG_SRCDMA_SIZE	0x40
+
 struct zynq_fpga_priv {
 	int irq;
 	struct clk *clk;
@@ -140,6 +184,7 @@ struct zynq_fpga_priv {
 	struct scatterlist *cur_sg;
 
 	struct completion dma_done;
+	struct mutex mutex_lock;
 };
 
 static inline void zynq_fpga_write(struct zynq_fpga_priv *priv, u32 offset,
@@ -164,6 +209,15 @@ static inline void zynq_fpga_set_irq(struct zynq_fpga_priv *priv, u32 enable)
 	zynq_fpga_write(priv, INT_MASK_OFFSET, ~enable);
 }
 
+static void zynq_fpga_dma_xfer(struct zynq_fpga_priv *priv, u32 srcaddr,
+			       u32 srclen, u32 dstaddr, u32 dstlen)
+{
+	zynq_fpga_write(priv, DMA_SRC_ADDR_OFFSET, srcaddr);
+	zynq_fpga_write(priv, DMA_DST_ADDR_OFFSET, dstaddr);
+	zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, srclen);
+	zynq_fpga_write(priv, DMA_DEST_LEN_OFFSET, dstlen);
+}
+
 /* Must be called with dma_lock held */
 static void zynq_step_dma(struct zynq_fpga_priv *priv)
 {
@@ -546,12 +600,211 @@ static enum fpga_mgr_states zynq_fpga_ops_state(struct fpga_manager *mgr)
 	return FPGA_MGR_STATE_UNKNOWN;
 }
 
+static int zynq_type1_pkt(u8 reg, u8 opcode, u16 size)
+{
+	/*
+	 * Type 1 Packet Header Format
+	 * The header section is always a 32-bit word.
+	 *
+	 * HeaderType | Opcode | Register Address | Reserved | Word Count
+	 * [31:29]      [28:27]         [26:13]      [12:11]     [10:0]
+	 * --------------------------------------------------------------
+	 *   001          xx      RRRRRRRRRxxxxx        RR      xxxxxxxxxxx
+	 *
+	 * @R: means the bit is not used and reserved for future use.
+	 * The reserved bits should be written as 0s.
+	 *
+	 * Generating the Type 1 packet header which involves sifting of Type1
+	 * Header Mask, Register value and the OpCode which is 01 in this case
+	 * as only read operation is to be carried out and then performing OR
+	 * operation with the Word Length.
+	 */
+	return (((1 << TYPE1_HDR_SHIFT) |
+		(reg << TYPE1_REG_SHIFT) |
+		(opcode << TYPE1_OP_SHIFT)) | size);
+
+}
+
+static int zynq_fpga_getconfigreg(struct fpga_manager *mgr, u8 reg)
+{
+	struct zynq_fpga_priv *priv;
+	int ret = 0, cmdindex, src_offset;
+	int *srcbuf, *dstbuf;
+	dma_addr_t src_dma_addr, dst_dma_addr;
+	u32 status, intr_status;
+
+	priv = mgr->priv;
+
+	srcbuf = dma_alloc_coherent(mgr->dev.parent, CFGREG_SRCDMA_SIZE,
+				    &src_dma_addr, GFP_KERNEL);
+	if (!srcbuf)
+		return -ENOMEM;
+
+	dstbuf = dma_alloc_coherent(mgr->dev.parent, sizeof(dstbuf),
+				    &dst_dma_addr, GFP_KERNEL);
+	if (!dstbuf)
+		goto free_srcbuf;
+
+	cmdindex = 0;
+	srcbuf[cmdindex++] = DUMMY_WORD_MASK;
+	srcbuf[cmdindex++] = BUSWIDTH_SYNCWORD_MASK;
+	srcbuf[cmdindex++] = BUSWIDTH_DETECT_MASK;
+	srcbuf[cmdindex++] = DUMMY_WORD_MASK;
+	srcbuf[cmdindex++] = SYNC_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = zynq_type1_pkt(reg, TYPE1_OPCODE_READ, 1);
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+
+	ret = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status,
+				     status & STATUS_PCFG_INIT_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for PCFG_INIT\n");
+		goto free_dstbuf;
+	}
+
+	intr_status = zynq_fpga_read(priv, INT_STS_OFFSET);
+	zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
+
+	zynq_fpga_dma_xfer(priv, src_dma_addr, cmdindex,
+			   DMA_INVALID_ADDRESS, 0);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+	zynq_fpga_set_irq(priv, intr_status);
+	zynq_fpga_dma_xfer(priv, DMA_INVALID_ADDRESS, 0, dst_dma_addr, 1);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_DMA_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for DMA DONE\n");
+		goto free_dstbuf;
+	}
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+	src_offset = cmdindex * 4;
+	cmdindex = 0;
+	srcbuf[cmdindex++] = zynq_type1_pkt(TYPE1_CMD_OFFSET,
+					    TYPE1_OPCODE_WRITE, 1);
+	srcbuf[cmdindex++] = DESYNC_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	zynq_fpga_dma_xfer(priv, src_dma_addr + src_offset, cmdindex,
+			   DMA_INVALID_ADDRESS, 0);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_DMA_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for DMA DONE\n");
+		goto free_dstbuf;
+	}
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+
+	ret = *dstbuf;
+
+free_dstbuf:
+	dma_free_coherent(mgr->dev.parent, sizeof(dstbuf), dstbuf,
+			  dst_dma_addr);
+free_srcbuf:
+	dma_free_coherent(mgr->dev.parent, CFGREG_SRCDMA_SIZE, srcbuf,
+			  src_dma_addr);
+
+	return ret;
+}
+
+static int zynq_fpga_ops_read(struct fpga_manager *mgr, struct seq_file *s)
+{
+	struct zynq_fpga_priv *priv;
+	int err;
+
+	priv = mgr->priv;
+
+	if (!mutex_trylock(&priv->mutex_lock))
+		return -EBUSY;
+
+	err = clk_enable(priv->clk);
+	if (err)
+		goto err_unlock;
+
+	seq_puts(s, "zynq FPGA Configuration register contents are\n");
+	seq_printf(s, "CRC --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CRC_OFFSET)));
+	seq_printf(s, "FAR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FAR_OFFSET)));
+	seq_printf(s, "FDRI --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FDRI_OFFSET)));
+	seq_printf(s, "FDRO --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FDRO_OFFSET)));
+	seq_printf(s, "CMD --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CMD_OFFSET)));
+	seq_printf(s, "CTRL0 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CTRL0_OFFSET)));
+	seq_printf(s, "MASK --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_MASK_OFFSET)));
+	seq_printf(s, "STAT --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_STAT_OFFSET)));
+	seq_printf(s, "LOUT --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_LOUT_OFFSET)));
+	seq_printf(s, "COR0 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_COR0_OFFSET)));
+	seq_printf(s, "MFWR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_MFWR_OFFSET)));
+	seq_printf(s, "CBC --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CBC_OFFSET)));
+	seq_printf(s, "IDCODE --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_IDCODE_OFFSET)));
+	seq_printf(s, "AXSS --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_AXSS_OFFSET)));
+	seq_printf(s, "COR1 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_COR1_OFFSET)));
+	seq_printf(s, "WBSTR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_WBSTR_OFFSET)));
+	seq_printf(s, "TIMER --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_TIMER_OFFSET)));
+	seq_printf(s, "BOOTSTS --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_BOOTSTS_OFFSET)));
+	seq_printf(s, "CTRL1 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CTRL1_OFFSET)));
+	seq_printf(s, "BSPI --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_BSPI_OFFSET)));
+
+	clk_disable(priv->clk);
+
+err_unlock:
+	mutex_unlock(&priv->mutex_lock);
+
+	return 0;
+}
+
 static const struct fpga_manager_ops zynq_fpga_ops = {
 	.initial_header_size = 128,
 	.state = zynq_fpga_ops_state,
 	.write_init = zynq_fpga_ops_write_init,
 	.write_sg = zynq_fpga_ops_write,
 	.write_complete = zynq_fpga_ops_write_complete,
+	.read = zynq_fpga_ops_read,
 };
 
 static int zynq_fpga_probe(struct platform_device *pdev)
@@ -565,6 +818,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
 	if (!priv)
 		return -ENOMEM;
 	spin_lock_init(&priv->dma_lock);
+	mutex_init(&priv->mutex_lock);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	priv->io_base = devm_ioremap_resource(dev, res);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [RFC PATCH 2/2] fpga: zynq-fpga: Add support for readback of configuration registers
@ 2018-07-03  8:44 ` Appana Durga Kedareswara rao
  0 siblings, 0 replies; 3+ messages in thread
From: Appana Durga Kedareswara rao @ 2018-07-03  8:44 UTC (permalink / raw)
  To: atull, mdf, michal.simek, navam
  Cc: linux-fpga, linux-arm-kernel, linux-kernel, Appana Durga Kedareswara rao

This patch adds support for Read-back of
configuration registers in zynq.

Signed-off-by: Appana Durga Kedareswara rao <appana.durga.rao@xilinx.com>
---
 drivers/fpga/zynq-fpga.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 254 insertions(+)

diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index 70b15b3..31d39f0 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -25,6 +25,7 @@
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/pm.h>
@@ -127,6 +128,49 @@
 /* Disable global resets */
 #define FPGA_RST_NONE_MASK		0x0
 
+/* Configuration Registers */
+#define TYPE1_CRC_OFFSET	0x0
+#define TYPE1_FAR_OFFSET	0x1
+#define TYPE1_FDRI_OFFSET	0x2
+#define TYPE1_FDRO_OFFSET	0x3
+#define TYPE1_CMD_OFFSET	0x4
+#define TYPE1_CTRL0_OFFSET	0x5
+#define TYPE1_MASK_OFFSET	0x6
+#define TYPE1_STAT_OFFSET	0x7
+#define TYPE1_LOUT_OFFSET	0x8
+#define TYPE1_COR0_OFFSET	0x9
+#define TYPE1_MFWR_OFFSET	0xa
+#define TYPE1_CBC_OFFSET	0xb
+#define TYPE1_IDCODE_OFFSET	0xc
+#define TYPE1_AXSS_OFFSET	0xd
+#define TYPE1_COR1_OFFSET	0xe
+#define TYPE1_WBSTR_OFFSET	0x10
+#define TYPE1_TIMER_OFFSET	0x11
+#define TYPE1_BOOTSTS_OFFSET	0x16
+#define TYPE1_CTRL1_OFFSET	0x18
+#define TYPE1_BSPI_OFFSET	0x1f
+
+/* Masks for Configuration registers */
+#define RCFG_CMD_MASK		0x00000004
+#define START_CMD_MASK		0x00000005
+#define RCRC_CMD_MASK		0x00000007
+#define SHUTDOWN_CMD_MASK	0x0000000B
+#define DESYNC_WORD_MASK	0x0000000D
+#define BUSWIDTH_SYNCWORD_MASK	0x000000BB
+#define NOOP_WORD_MASK		0x20000000
+#define BUSWIDTH_DETECT_MASK	0x11220044
+#define SYNC_WORD_MASK		0xAA995566
+#define DUMMY_WORD_MASK		0xFFFFFFFF
+
+#define TYPE1_HDR_SHIFT		29
+#define TYPE1_REG_SHIFT		13
+#define TYPE1_OP_SHIFT		27
+#define TYPE1_OPCODE_NOOP	0
+#define TYPE1_OPCODE_READ	1
+#define TYPE1_OPCODE_WRITE	2
+
+#define CFGREG_SRCDMA_SIZE	0x40
+
 struct zynq_fpga_priv {
 	int irq;
 	struct clk *clk;
@@ -140,6 +184,7 @@ struct zynq_fpga_priv {
 	struct scatterlist *cur_sg;
 
 	struct completion dma_done;
+	struct mutex mutex_lock;
 };
 
 static inline void zynq_fpga_write(struct zynq_fpga_priv *priv, u32 offset,
@@ -164,6 +209,15 @@ static inline void zynq_fpga_set_irq(struct zynq_fpga_priv *priv, u32 enable)
 	zynq_fpga_write(priv, INT_MASK_OFFSET, ~enable);
 }
 
+static void zynq_fpga_dma_xfer(struct zynq_fpga_priv *priv, u32 srcaddr,
+			       u32 srclen, u32 dstaddr, u32 dstlen)
+{
+	zynq_fpga_write(priv, DMA_SRC_ADDR_OFFSET, srcaddr);
+	zynq_fpga_write(priv, DMA_DST_ADDR_OFFSET, dstaddr);
+	zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, srclen);
+	zynq_fpga_write(priv, DMA_DEST_LEN_OFFSET, dstlen);
+}
+
 /* Must be called with dma_lock held */
 static void zynq_step_dma(struct zynq_fpga_priv *priv)
 {
@@ -546,12 +600,211 @@ static enum fpga_mgr_states zynq_fpga_ops_state(struct fpga_manager *mgr)
 	return FPGA_MGR_STATE_UNKNOWN;
 }
 
+static int zynq_type1_pkt(u8 reg, u8 opcode, u16 size)
+{
+	/*
+	 * Type 1 Packet Header Format
+	 * The header section is always a 32-bit word.
+	 *
+	 * HeaderType | Opcode | Register Address | Reserved | Word Count
+	 * [31:29]      [28:27]         [26:13]      [12:11]     [10:0]
+	 * --------------------------------------------------------------
+	 *   001          xx      RRRRRRRRRxxxxx        RR      xxxxxxxxxxx
+	 *
+	 * @R: means the bit is not used and reserved for future use.
+	 * The reserved bits should be written as 0s.
+	 *
+	 * Generating the Type 1 packet header which involves sifting of Type1
+	 * Header Mask, Register value and the OpCode which is 01 in this case
+	 * as only read operation is to be carried out and then performing OR
+	 * operation with the Word Length.
+	 */
+	return (((1 << TYPE1_HDR_SHIFT) |
+		(reg << TYPE1_REG_SHIFT) |
+		(opcode << TYPE1_OP_SHIFT)) | size);
+
+}
+
+static int zynq_fpga_getconfigreg(struct fpga_manager *mgr, u8 reg)
+{
+	struct zynq_fpga_priv *priv;
+	int ret = 0, cmdindex, src_offset;
+	int *srcbuf, *dstbuf;
+	dma_addr_t src_dma_addr, dst_dma_addr;
+	u32 status, intr_status;
+
+	priv = mgr->priv;
+
+	srcbuf = dma_alloc_coherent(mgr->dev.parent, CFGREG_SRCDMA_SIZE,
+				    &src_dma_addr, GFP_KERNEL);
+	if (!srcbuf)
+		return -ENOMEM;
+
+	dstbuf = dma_alloc_coherent(mgr->dev.parent, sizeof(dstbuf),
+				    &dst_dma_addr, GFP_KERNEL);
+	if (!dstbuf)
+		goto free_srcbuf;
+
+	cmdindex = 0;
+	srcbuf[cmdindex++] = DUMMY_WORD_MASK;
+	srcbuf[cmdindex++] = BUSWIDTH_SYNCWORD_MASK;
+	srcbuf[cmdindex++] = BUSWIDTH_DETECT_MASK;
+	srcbuf[cmdindex++] = DUMMY_WORD_MASK;
+	srcbuf[cmdindex++] = SYNC_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = zynq_type1_pkt(reg, TYPE1_OPCODE_READ, 1);
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+
+	ret = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status,
+				     status & STATUS_PCFG_INIT_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for PCFG_INIT\n");
+		goto free_dstbuf;
+	}
+
+	intr_status = zynq_fpga_read(priv, INT_STS_OFFSET);
+	zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
+
+	zynq_fpga_dma_xfer(priv, src_dma_addr, cmdindex,
+			   DMA_INVALID_ADDRESS, 0);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+	zynq_fpga_set_irq(priv, intr_status);
+	zynq_fpga_dma_xfer(priv, DMA_INVALID_ADDRESS, 0, dst_dma_addr, 1);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_DMA_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for DMA DONE\n");
+		goto free_dstbuf;
+	}
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+	src_offset = cmdindex * 4;
+	cmdindex = 0;
+	srcbuf[cmdindex++] = zynq_type1_pkt(TYPE1_CMD_OFFSET,
+					    TYPE1_OPCODE_WRITE, 1);
+	srcbuf[cmdindex++] = DESYNC_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	zynq_fpga_dma_xfer(priv, src_dma_addr + src_offset, cmdindex,
+			   DMA_INVALID_ADDRESS, 0);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_DMA_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for DMA DONE\n");
+		goto free_dstbuf;
+	}
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+
+	ret = *dstbuf;
+
+free_dstbuf:
+	dma_free_coherent(mgr->dev.parent, sizeof(dstbuf), dstbuf,
+			  dst_dma_addr);
+free_srcbuf:
+	dma_free_coherent(mgr->dev.parent, CFGREG_SRCDMA_SIZE, srcbuf,
+			  src_dma_addr);
+
+	return ret;
+}
+
+static int zynq_fpga_ops_read(struct fpga_manager *mgr, struct seq_file *s)
+{
+	struct zynq_fpga_priv *priv;
+	int err;
+
+	priv = mgr->priv;
+
+	if (!mutex_trylock(&priv->mutex_lock))
+		return -EBUSY;
+
+	err = clk_enable(priv->clk);
+	if (err)
+		goto err_unlock;
+
+	seq_puts(s, "zynq FPGA Configuration register contents are\n");
+	seq_printf(s, "CRC --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CRC_OFFSET)));
+	seq_printf(s, "FAR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FAR_OFFSET)));
+	seq_printf(s, "FDRI --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FDRI_OFFSET)));
+	seq_printf(s, "FDRO --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FDRO_OFFSET)));
+	seq_printf(s, "CMD --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CMD_OFFSET)));
+	seq_printf(s, "CTRL0 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CTRL0_OFFSET)));
+	seq_printf(s, "MASK --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_MASK_OFFSET)));
+	seq_printf(s, "STAT --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_STAT_OFFSET)));
+	seq_printf(s, "LOUT --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_LOUT_OFFSET)));
+	seq_printf(s, "COR0 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_COR0_OFFSET)));
+	seq_printf(s, "MFWR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_MFWR_OFFSET)));
+	seq_printf(s, "CBC --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CBC_OFFSET)));
+	seq_printf(s, "IDCODE --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_IDCODE_OFFSET)));
+	seq_printf(s, "AXSS --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_AXSS_OFFSET)));
+	seq_printf(s, "COR1 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_COR1_OFFSET)));
+	seq_printf(s, "WBSTR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_WBSTR_OFFSET)));
+	seq_printf(s, "TIMER --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_TIMER_OFFSET)));
+	seq_printf(s, "BOOTSTS --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_BOOTSTS_OFFSET)));
+	seq_printf(s, "CTRL1 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CTRL1_OFFSET)));
+	seq_printf(s, "BSPI --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_BSPI_OFFSET)));
+
+	clk_disable(priv->clk);
+
+err_unlock:
+	mutex_unlock(&priv->mutex_lock);
+
+	return 0;
+}
+
 static const struct fpga_manager_ops zynq_fpga_ops = {
 	.initial_header_size = 128,
 	.state = zynq_fpga_ops_state,
 	.write_init = zynq_fpga_ops_write_init,
 	.write_sg = zynq_fpga_ops_write,
 	.write_complete = zynq_fpga_ops_write_complete,
+	.read = zynq_fpga_ops_read,
 };
 
 static int zynq_fpga_probe(struct platform_device *pdev)
@@ -565,6 +818,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
 	if (!priv)
 		return -ENOMEM;
 	spin_lock_init(&priv->dma_lock);
+	mutex_init(&priv->mutex_lock);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	priv->io_base = devm_ioremap_resource(dev, res);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [RFC PATCH 2/2] fpga: zynq-fpga: Add support for readback of configuration registers
@ 2018-07-03  8:44 ` Appana Durga Kedareswara rao
  0 siblings, 0 replies; 3+ messages in thread
From: Appana Durga Kedareswara rao @ 2018-07-03  8:44 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for Read-back of
configuration registers in zynq.

Signed-off-by: Appana Durga Kedareswara rao <appana.durga.rao@xilinx.com>
---
 drivers/fpga/zynq-fpga.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 254 insertions(+)

diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index 70b15b3..31d39f0 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -25,6 +25,7 @@
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/pm.h>
@@ -127,6 +128,49 @@
 /* Disable global resets */
 #define FPGA_RST_NONE_MASK		0x0
 
+/* Configuration Registers */
+#define TYPE1_CRC_OFFSET	0x0
+#define TYPE1_FAR_OFFSET	0x1
+#define TYPE1_FDRI_OFFSET	0x2
+#define TYPE1_FDRO_OFFSET	0x3
+#define TYPE1_CMD_OFFSET	0x4
+#define TYPE1_CTRL0_OFFSET	0x5
+#define TYPE1_MASK_OFFSET	0x6
+#define TYPE1_STAT_OFFSET	0x7
+#define TYPE1_LOUT_OFFSET	0x8
+#define TYPE1_COR0_OFFSET	0x9
+#define TYPE1_MFWR_OFFSET	0xa
+#define TYPE1_CBC_OFFSET	0xb
+#define TYPE1_IDCODE_OFFSET	0xc
+#define TYPE1_AXSS_OFFSET	0xd
+#define TYPE1_COR1_OFFSET	0xe
+#define TYPE1_WBSTR_OFFSET	0x10
+#define TYPE1_TIMER_OFFSET	0x11
+#define TYPE1_BOOTSTS_OFFSET	0x16
+#define TYPE1_CTRL1_OFFSET	0x18
+#define TYPE1_BSPI_OFFSET	0x1f
+
+/* Masks for Configuration registers */
+#define RCFG_CMD_MASK		0x00000004
+#define START_CMD_MASK		0x00000005
+#define RCRC_CMD_MASK		0x00000007
+#define SHUTDOWN_CMD_MASK	0x0000000B
+#define DESYNC_WORD_MASK	0x0000000D
+#define BUSWIDTH_SYNCWORD_MASK	0x000000BB
+#define NOOP_WORD_MASK		0x20000000
+#define BUSWIDTH_DETECT_MASK	0x11220044
+#define SYNC_WORD_MASK		0xAA995566
+#define DUMMY_WORD_MASK		0xFFFFFFFF
+
+#define TYPE1_HDR_SHIFT		29
+#define TYPE1_REG_SHIFT		13
+#define TYPE1_OP_SHIFT		27
+#define TYPE1_OPCODE_NOOP	0
+#define TYPE1_OPCODE_READ	1
+#define TYPE1_OPCODE_WRITE	2
+
+#define CFGREG_SRCDMA_SIZE	0x40
+
 struct zynq_fpga_priv {
 	int irq;
 	struct clk *clk;
@@ -140,6 +184,7 @@ struct zynq_fpga_priv {
 	struct scatterlist *cur_sg;
 
 	struct completion dma_done;
+	struct mutex mutex_lock;
 };
 
 static inline void zynq_fpga_write(struct zynq_fpga_priv *priv, u32 offset,
@@ -164,6 +209,15 @@ static inline void zynq_fpga_set_irq(struct zynq_fpga_priv *priv, u32 enable)
 	zynq_fpga_write(priv, INT_MASK_OFFSET, ~enable);
 }
 
+static void zynq_fpga_dma_xfer(struct zynq_fpga_priv *priv, u32 srcaddr,
+			       u32 srclen, u32 dstaddr, u32 dstlen)
+{
+	zynq_fpga_write(priv, DMA_SRC_ADDR_OFFSET, srcaddr);
+	zynq_fpga_write(priv, DMA_DST_ADDR_OFFSET, dstaddr);
+	zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, srclen);
+	zynq_fpga_write(priv, DMA_DEST_LEN_OFFSET, dstlen);
+}
+
 /* Must be called with dma_lock held */
 static void zynq_step_dma(struct zynq_fpga_priv *priv)
 {
@@ -546,12 +600,211 @@ static enum fpga_mgr_states zynq_fpga_ops_state(struct fpga_manager *mgr)
 	return FPGA_MGR_STATE_UNKNOWN;
 }
 
+static int zynq_type1_pkt(u8 reg, u8 opcode, u16 size)
+{
+	/*
+	 * Type 1 Packet Header Format
+	 * The header section is always a 32-bit word.
+	 *
+	 * HeaderType | Opcode | Register Address | Reserved | Word Count
+	 * [31:29]      [28:27]         [26:13]      [12:11]     [10:0]
+	 * --------------------------------------------------------------
+	 *   001          xx      RRRRRRRRRxxxxx        RR      xxxxxxxxxxx
+	 *
+	 * @R: means the bit is not used and reserved for future use.
+	 * The reserved bits should be written as 0s.
+	 *
+	 * Generating the Type 1 packet header which involves sifting of Type1
+	 * Header Mask, Register value and the OpCode which is 01 in this case
+	 * as only read operation is to be carried out and then performing OR
+	 * operation with the Word Length.
+	 */
+	return (((1 << TYPE1_HDR_SHIFT) |
+		(reg << TYPE1_REG_SHIFT) |
+		(opcode << TYPE1_OP_SHIFT)) | size);
+
+}
+
+static int zynq_fpga_getconfigreg(struct fpga_manager *mgr, u8 reg)
+{
+	struct zynq_fpga_priv *priv;
+	int ret = 0, cmdindex, src_offset;
+	int *srcbuf, *dstbuf;
+	dma_addr_t src_dma_addr, dst_dma_addr;
+	u32 status, intr_status;
+
+	priv = mgr->priv;
+
+	srcbuf = dma_alloc_coherent(mgr->dev.parent, CFGREG_SRCDMA_SIZE,
+				    &src_dma_addr, GFP_KERNEL);
+	if (!srcbuf)
+		return -ENOMEM;
+
+	dstbuf = dma_alloc_coherent(mgr->dev.parent, sizeof(dstbuf),
+				    &dst_dma_addr, GFP_KERNEL);
+	if (!dstbuf)
+		goto free_srcbuf;
+
+	cmdindex = 0;
+	srcbuf[cmdindex++] = DUMMY_WORD_MASK;
+	srcbuf[cmdindex++] = BUSWIDTH_SYNCWORD_MASK;
+	srcbuf[cmdindex++] = BUSWIDTH_DETECT_MASK;
+	srcbuf[cmdindex++] = DUMMY_WORD_MASK;
+	srcbuf[cmdindex++] = SYNC_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = zynq_type1_pkt(reg, TYPE1_OPCODE_READ, 1);
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+
+	ret = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status,
+				     status & STATUS_PCFG_INIT_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for PCFG_INIT\n");
+		goto free_dstbuf;
+	}
+
+	intr_status = zynq_fpga_read(priv, INT_STS_OFFSET);
+	zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
+
+	zynq_fpga_dma_xfer(priv, src_dma_addr, cmdindex,
+			   DMA_INVALID_ADDRESS, 0);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+	zynq_fpga_set_irq(priv, intr_status);
+	zynq_fpga_dma_xfer(priv, DMA_INVALID_ADDRESS, 0, dst_dma_addr, 1);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_DMA_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for DMA DONE\n");
+		goto free_dstbuf;
+	}
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+	src_offset = cmdindex * 4;
+	cmdindex = 0;
+	srcbuf[cmdindex++] = zynq_type1_pkt(TYPE1_CMD_OFFSET,
+					    TYPE1_OPCODE_WRITE, 1);
+	srcbuf[cmdindex++] = DESYNC_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	srcbuf[cmdindex++] = NOOP_WORD_MASK;
+	zynq_fpga_dma_xfer(priv, src_dma_addr + src_offset, cmdindex,
+			   DMA_INVALID_ADDRESS, 0);
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_DMA_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for DMA DONE\n");
+		goto free_dstbuf;
+	}
+	ret = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, status,
+				     status & IXR_D_P_DONE_MASK,
+				     INIT_POLL_DELAY,
+				     INIT_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(&mgr->dev, "Timeout waiting for D_P_DONE\n");
+		goto free_dstbuf;
+	}
+
+	ret = *dstbuf;
+
+free_dstbuf:
+	dma_free_coherent(mgr->dev.parent, sizeof(dstbuf), dstbuf,
+			  dst_dma_addr);
+free_srcbuf:
+	dma_free_coherent(mgr->dev.parent, CFGREG_SRCDMA_SIZE, srcbuf,
+			  src_dma_addr);
+
+	return ret;
+}
+
+static int zynq_fpga_ops_read(struct fpga_manager *mgr, struct seq_file *s)
+{
+	struct zynq_fpga_priv *priv;
+	int err;
+
+	priv = mgr->priv;
+
+	if (!mutex_trylock(&priv->mutex_lock))
+		return -EBUSY;
+
+	err = clk_enable(priv->clk);
+	if (err)
+		goto err_unlock;
+
+	seq_puts(s, "zynq FPGA Configuration register contents are\n");
+	seq_printf(s, "CRC --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CRC_OFFSET)));
+	seq_printf(s, "FAR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FAR_OFFSET)));
+	seq_printf(s, "FDRI --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FDRI_OFFSET)));
+	seq_printf(s, "FDRO --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_FDRO_OFFSET)));
+	seq_printf(s, "CMD --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CMD_OFFSET)));
+	seq_printf(s, "CTRL0 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CTRL0_OFFSET)));
+	seq_printf(s, "MASK --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_MASK_OFFSET)));
+	seq_printf(s, "STAT --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_STAT_OFFSET)));
+	seq_printf(s, "LOUT --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_LOUT_OFFSET)));
+	seq_printf(s, "COR0 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_COR0_OFFSET)));
+	seq_printf(s, "MFWR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_MFWR_OFFSET)));
+	seq_printf(s, "CBC --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CBC_OFFSET)));
+	seq_printf(s, "IDCODE --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_IDCODE_OFFSET)));
+	seq_printf(s, "AXSS --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_AXSS_OFFSET)));
+	seq_printf(s, "COR1 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_COR1_OFFSET)));
+	seq_printf(s, "WBSTR --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_WBSTR_OFFSET)));
+	seq_printf(s, "TIMER --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_TIMER_OFFSET)));
+	seq_printf(s, "BOOTSTS --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_BOOTSTS_OFFSET)));
+	seq_printf(s, "CTRL1 --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_CTRL1_OFFSET)));
+	seq_printf(s, "BSPI --> \t %x \t\r\n",
+		   (zynq_fpga_getconfigreg(mgr, TYPE1_BSPI_OFFSET)));
+
+	clk_disable(priv->clk);
+
+err_unlock:
+	mutex_unlock(&priv->mutex_lock);
+
+	return 0;
+}
+
 static const struct fpga_manager_ops zynq_fpga_ops = {
 	.initial_header_size = 128,
 	.state = zynq_fpga_ops_state,
 	.write_init = zynq_fpga_ops_write_init,
 	.write_sg = zynq_fpga_ops_write,
 	.write_complete = zynq_fpga_ops_write_complete,
+	.read = zynq_fpga_ops_read,
 };
 
 static int zynq_fpga_probe(struct platform_device *pdev)
@@ -565,6 +818,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
 	if (!priv)
 		return -ENOMEM;
 	spin_lock_init(&priv->dma_lock);
+	mutex_init(&priv->mutex_lock);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	priv->io_base = devm_ioremap_resource(dev, res);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2018-07-03  8:45 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-03  8:44 [RFC PATCH 2/2] fpga: zynq-fpga: Add support for readback of configuration registers Appana Durga Kedareswara rao
2018-07-03  8:44 ` Appana Durga Kedareswara rao
2018-07-03  8:44 ` Appana Durga Kedareswara rao

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.