This patch adds support for Read-back of configuration registers in zynq. Signed-off-by: Appana Durga Kedareswara rao <appana.durga....@xilinx.com> --- Changes for v2: --> Removed locks from the read ops as lock handling is done in the framework.
drivers/fpga/zynq-fpga.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 70b15b3..b56a6c6 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -127,6 +127,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; @@ -164,6 +207,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 +598,205 @@ 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; + + err = clk_enable(priv->clk); + if (err) + return err; + + 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); + + 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) -- 2.7.4