Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1032348AbcJRVC4 (ORCPT ); Tue, 18 Oct 2016 17:02:56 -0400 Received: from mail-wm0-f48.google.com ([74.125.82.48]:37077 "EHLO mail-wm0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S935236AbcJRVCF (ORCPT ); Tue, 18 Oct 2016 17:02:05 -0400 Date: Tue, 18 Oct 2016 23:01:59 +0200 From: Moritz Fischer To: Alan Tull Cc: Rob Herring , Frank Rowand , Mark Rutland , Greg Kroah-Hartman , Ian Campbell , Jon Masters , Michal Simek , Jonathan Corbet , Cyril Chemparathy , Matthew Gerlach , Dinh Nguyen , devicetree@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, delicious.quinoa@gmail.com Subject: Re: [PATCH v20 10/10] fpga-manager: Add Socfpga Arria10 support Message-ID: <20161018210159.GA1916@live.com> References: <20161017160941.4205-1-atull@opensource.altera.com> <20161017160941.4205-11-atull@opensource.altera.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20161017160941.4205-11-atull@opensource.altera.com> User-Agent: Mutt/1.7.1 (2016-10-04) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 19595 Lines: 620 On Mon, Oct 17, 2016 at 11:09:41AM -0500, Alan Tull wrote: > Add low level driver to support reprogramming FPGAs for Altera > SoCFPGA Arria10. > > Signed-off-by: Alan Tull Reviewed-by: Moritz Fischer > --- > v19: Added to this patchset as has been changed to use > fpga image information struct > a checkpatch fix of a block comment > do not use clk_put because we are using devm_clk_get > v20: use regmap_read_poll_timeout > Add some macros/comments > remove some debug prints > --- > drivers/fpga/Kconfig | 6 + > drivers/fpga/Makefile | 1 + > drivers/fpga/socfpga-a10.c | 556 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 563 insertions(+) > create mode 100644 drivers/fpga/socfpga-a10.c > > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig > index 8fe6a84..889e4c3 100644 > --- a/drivers/fpga/Kconfig > +++ b/drivers/fpga/Kconfig > @@ -26,6 +26,12 @@ config FPGA_MGR_SOCFPGA > help > FPGA manager driver support for Altera SOCFPGA. > > +config FPGA_MGR_SOCFPGA_A10 > + tristate "Altera SoCFPGA Arria10" > + depends on ARCH_SOCFPGA > + help > + FPGA manager driver support for Altera Arria10 SoCFPGA. > + > config FPGA_MGR_ZYNQ_FPGA > tristate "Xilinx Zynq FPGA" > depends on ARCH_ZYNQ || COMPILE_TEST > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile > index a6f874d..8df07bc 100644 > --- a/drivers/fpga/Makefile > +++ b/drivers/fpga/Makefile > @@ -7,6 +7,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o > > # FPGA Manager Drivers > obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o > +obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o > obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o > > # FPGA Bridge Drivers > diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c > new file mode 100644 > index 0000000..ccd9fb2 > --- /dev/null > +++ b/drivers/fpga/socfpga-a10.c > @@ -0,0 +1,556 @@ > +/* > + * FPGA Manager Driver for Altera Arria10 SoCFPGA > + * > + * Copyright (C) 2015-2016 Altera Corporation > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see . > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define A10_FPGAMGR_DCLKCNT_OFST 0x08 > +#define A10_FPGAMGR_DCLKSTAT_OFST 0x0c > +#define A10_FPGAMGR_IMGCFG_CTL_00_OFST 0x70 > +#define A10_FPGAMGR_IMGCFG_CTL_01_OFST 0x74 > +#define A10_FPGAMGR_IMGCFG_CTL_02_OFST 0x78 > +#define A10_FPGAMGR_IMGCFG_STAT_OFST 0x80 > + > +#define A10_FPGAMGR_DCLKSTAT_DCLKDONE BIT(0) > + > +#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NCONFIG BIT(0) > +#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NSTATUS BIT(1) > +#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_CONDONE BIT(2) > +#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NCONFIG BIT(8) > +#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NSTATUS_OE BIT(16) > +#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_CONDONE_OE BIT(24) > + > +#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG BIT(0) > +#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST BIT(16) > +#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE BIT(24) > + > +#define A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL BIT(0) > +#define A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_MASK (BIT(16) | BIT(17)) > +#define A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_SHIFT 16 > +#define A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH BIT(24) > +#define A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH_SHIFT 24 > + > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_CRC_ERROR BIT(0) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_EARLY_USERMODE BIT(1) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE BIT(2) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN BIT(4) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_CONDONE_PIN BIT(6) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY BIT(9) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_DONE BIT(10) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR BIT(11) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_NCONFIG_PIN BIT(12) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_MASK (BIT(16) | BIT(17) | BIT(18)) > +#define A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_SHIFT 16 > + > +/* FPGA CD Ratio Value */ > +#define CDRATIO_x1 0x0 > +#define CDRATIO_x2 0x1 > +#define CDRATIO_x4 0x2 > +#define CDRATIO_x8 0x3 > + > +/* Configuration width 16/32 bit */ > +#define CFGWDTH_32 1 > +#define CFGWDTH_16 0 > + > +/* > + * struct a10_fpga_priv - private data for fpga manager > + * @regmap: regmap for register access > + * @fpga_data_addr: iomap for single address data register to FPGA > + * @clk: clock > + */ > +struct a10_fpga_priv { > + struct regmap *regmap; > + void __iomem *fpga_data_addr; > + struct clk *clk; > +}; > + > +static bool socfpga_a10_fpga_writeable_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case A10_FPGAMGR_DCLKCNT_OFST: > + case A10_FPGAMGR_DCLKSTAT_OFST: > + case A10_FPGAMGR_IMGCFG_CTL_00_OFST: > + case A10_FPGAMGR_IMGCFG_CTL_01_OFST: > + case A10_FPGAMGR_IMGCFG_CTL_02_OFST: > + return true; > + } > + return false; > +} > + > +static bool socfpga_a10_fpga_readable_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case A10_FPGAMGR_DCLKCNT_OFST: > + case A10_FPGAMGR_DCLKSTAT_OFST: > + case A10_FPGAMGR_IMGCFG_CTL_00_OFST: > + case A10_FPGAMGR_IMGCFG_CTL_01_OFST: > + case A10_FPGAMGR_IMGCFG_CTL_02_OFST: > + case A10_FPGAMGR_IMGCFG_STAT_OFST: > + return true; > + } > + return false; > +} > + > +static const struct regmap_config socfpga_a10_fpga_regmap_config = { > + .reg_bits = 32, > + .reg_stride = 4, > + .val_bits = 32, > + .writeable_reg = socfpga_a10_fpga_writeable_reg, > + .readable_reg = socfpga_a10_fpga_readable_reg, > + .max_register = A10_FPGAMGR_IMGCFG_STAT_OFST, > + .cache_type = REGCACHE_NONE, > +}; > + > +/* > + * from the register map description of cdratio in imgcfg_ctrl_02: > + * Normal Configuration : 32bit Passive Parallel > + * Partial Reconfiguration : 16bit Passive Parallel > + */ > +static void socfpga_a10_fpga_set_cfg_width(struct a10_fpga_priv *priv, > + int width) > +{ > + width <<= A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH_SHIFT; > + > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST, > + A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH, width); > +} > + > +static void socfpga_a10_fpga_generate_dclks(struct a10_fpga_priv *priv, > + u32 count) > +{ > + u32 val; > + > + /* Clear any existing DONE status. */ > + regmap_write(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST, > + A10_FPGAMGR_DCLKSTAT_DCLKDONE); > + > + /* Issue the DCLK regmap. */ > + regmap_write(priv->regmap, A10_FPGAMGR_DCLKCNT_OFST, count); > + > + /* wait till the dclkcnt done */ > + regmap_read_poll_timeout(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST, val, > + val, 1, 100); > + > + /* Clear DONE status. */ > + regmap_write(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST, > + A10_FPGAMGR_DCLKSTAT_DCLKDONE); > +} > + > +#define RBF_ENCRYPTION_MODE_OFFSET 69 > +#define RBF_DECOMPRESS_OFFSET 229 > + > +static int socfpga_a10_fpga_encrypted(u32 *buf32, size_t buf32_size) > +{ > + if (buf32_size < RBF_ENCRYPTION_MODE_OFFSET + 1) > + return -EINVAL; > + > + /* Is the bitstream encrypted? */ > + return ((buf32[RBF_ENCRYPTION_MODE_OFFSET] >> 2) & 3) != 0; > +} > + > +static int socfpga_a10_fpga_compressed(u32 *buf32, size_t buf32_size) > +{ > + if (buf32_size < RBF_DECOMPRESS_OFFSET + 1) > + return -EINVAL; > + > + /* Is the bitstream compressed? */ > + return !((buf32[RBF_DECOMPRESS_OFFSET] >> 1) & 1); > +} > + > +static unsigned int socfpga_a10_fpga_get_cd_ratio(unsigned int cfg_width, > + bool encrypt, bool compress) > +{ > + unsigned int cd_ratio; > + > + /* > + * cd ratio is dependent on cfg width and whether the bitstream > + * is encrypted and/or compressed. > + * > + * | width | encr. | compr. | cd ratio | > + * | 16 | 0 | 0 | 1 | > + * | 16 | 0 | 1 | 4 | > + * | 16 | 1 | 0 | 2 | > + * | 16 | 1 | 1 | 4 | > + * | 32 | 0 | 0 | 1 | > + * | 32 | 0 | 1 | 8 | > + * | 32 | 1 | 0 | 4 | > + * | 32 | 1 | 1 | 8 | > + */ > + if (!compress && !encrypt) > + return CDRATIO_x1; > + > + if (compress) > + cd_ratio = CDRATIO_x4; > + else > + cd_ratio = CDRATIO_x2; > + > + /* If 32 bit, double the cd ratio by incrementing the field */ > + if (cfg_width == CFGWDTH_32) > + cd_ratio += 1; > + > + return cd_ratio; > +} > + > +static int socfpga_a10_fpga_set_cdratio(struct fpga_manager *mgr, > + unsigned int cfg_width, > + const char *buf, size_t count) > +{ > + struct a10_fpga_priv *priv = mgr->priv; > + unsigned int cd_ratio; > + int encrypt, compress; > + > + encrypt = socfpga_a10_fpga_encrypted((u32 *)buf, count / 4); > + if (encrypt < 0) > + return -EINVAL; > + > + compress = socfpga_a10_fpga_compressed((u32 *)buf, count / 4); > + if (compress < 0) > + return -EINVAL; > + > + cd_ratio = socfpga_a10_fpga_get_cd_ratio(cfg_width, encrypt, compress); > + > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST, > + A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_MASK, > + cd_ratio << A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_SHIFT); > + > + return 0; > +} > + > +static u32 socfpga_a10_fpga_read_stat(struct a10_fpga_priv *priv) > +{ > + u32 val; > + > + regmap_read(priv->regmap, A10_FPGAMGR_IMGCFG_STAT_OFST, &val); > + > + return val; > +} > + > +static int socfpga_a10_fpga_wait_for_pr_ready(struct a10_fpga_priv *priv) > +{ > + u32 reg, i; > + > + for (i = 0; i < 10 ; i++) { > + reg = socfpga_a10_fpga_read_stat(priv); > + > + if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR) > + return -EINVAL; > + > + if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY) > + return 0; > + } > + > + return -ETIMEDOUT; > +} > + > +static int socfpga_a10_fpga_wait_for_pr_done(struct a10_fpga_priv *priv) > +{ > + u32 reg, i; > + > + for (i = 0; i < 10 ; i++) { > + reg = socfpga_a10_fpga_read_stat(priv); > + > + if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR) > + return -EINVAL; > + > + if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_DONE) > + return 0; > + } > + > + return -ETIMEDOUT; > +} > + > +/* Start the FPGA programming by initialize the FPGA Manager */ > +static int socfpga_a10_fpga_write_init(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count) > +{ > + struct a10_fpga_priv *priv = mgr->priv; > + unsigned int cfg_width; > + u32 msel, stat, mask; > + int ret; > + > + if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) > + cfg_width = CFGWDTH_16; > + else > + return -EINVAL; > + > + /* Check for passive parallel (msel == 000 or 001) */ > + msel = socfpga_a10_fpga_read_stat(priv); > + msel &= A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_MASK; > + msel >>= A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_SHIFT; > + if ((msel != 0) && (msel != 1)) { > + dev_dbg(&mgr->dev, "Fail: invalid msel=%d\n", msel); > + return -EINVAL; > + } > + > + /* Make sure no external devices are interfering */ > + stat = socfpga_a10_fpga_read_stat(priv); > + mask = A10_FPGAMGR_IMGCFG_STAT_F2S_NCONFIG_PIN | > + A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN; > + if ((stat & mask) != mask) > + return -EINVAL; > + > + /* Set cfg width */ > + socfpga_a10_fpga_set_cfg_width(priv, cfg_width); > + > + /* Determine cd ratio from bitstream header and set cd ratio */ > + ret = socfpga_a10_fpga_set_cdratio(mgr, cfg_width, buf, count); > + if (ret) > + return ret; > + > + /* > + * Clear s2f_nce to enable chip select. Leave pr_request > + * unasserted and override disabled. > + */ > + regmap_write(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG); > + > + /* Set cfg_ctrl to enable s2f dclk and data */ > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST, > + A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL, > + A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL); > + > + /* > + * Disable overrides not needed for pr. > + * s2f_config==1 leaves reset deasseted. > + */ > + regmap_write(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_00_OFST, > + A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NCONFIG | > + A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NSTATUS | > + A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_CONDONE | > + A10_FPGAMGR_IMGCFG_CTL_00_S2F_NCONFIG); > + > + /* Enable override for data, dclk, nce, and pr_request to CSS */ > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG, 0); > + > + /* Send some clocks to clear out any errors */ > + socfpga_a10_fpga_generate_dclks(priv, 256); > + > + /* Assert pr_request */ > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST); > + > + /* Provide 2048 DCLKs before starting the config data streaming. */ > + socfpga_a10_fpga_generate_dclks(priv, 0x7ff); > + > + /* Wait for pr_ready */ > + return socfpga_a10_fpga_wait_for_pr_ready(priv); > +} > + > +/* > + * write data to the FPGA data register > + */ > +static int socfpga_a10_fpga_write(struct fpga_manager *mgr, const char *buf, > + size_t count) > +{ > + struct a10_fpga_priv *priv = mgr->priv; > + u32 *buffer_32 = (u32 *)buf; > + size_t i = 0; > + > + if (count <= 0) > + return -EINVAL; > + > + /* Write out the complete 32-bit chunks */ > + while (count >= sizeof(u32)) { > + writel(buffer_32[i++], priv->fpga_data_addr); > + count -= sizeof(u32); > + } > + > + /* Write out remaining non 32-bit chunks */ > + switch (count) { > + case 3: > + writel(buffer_32[i++] & 0x00ffffff, priv->fpga_data_addr); > + break; > + case 2: > + writel(buffer_32[i++] & 0x0000ffff, priv->fpga_data_addr); > + break; > + case 1: > + writel(buffer_32[i++] & 0x000000ff, priv->fpga_data_addr); > + break; > + case 0: > + break; > + default: > + /* This will never happen */ > + return -EFAULT; > + } > + > + return 0; > +} > + > +static int socfpga_a10_fpga_write_complete(struct fpga_manager *mgr, > + struct fpga_image_info *info) > +{ > + struct a10_fpga_priv *priv = mgr->priv; > + u32 reg; > + int ret; > + > + /* Wait for pr_done */ > + ret = socfpga_a10_fpga_wait_for_pr_done(priv); > + > + /* Clear pr_request */ > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST, 0); > + > + /* Send some clocks to clear out any errors */ > + socfpga_a10_fpga_generate_dclks(priv, 256); > + > + /* Disable s2f dclk and data */ > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST, > + A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL, 0); > + > + /* Deassert chip select */ > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE); > + > + /* Disable data, dclk, nce, and pr_request override to CSS */ > + regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG, > + A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG); > + > + /* Return any errors regarding pr_done or pr_error */ > + if (ret) > + return ret; > + > + /* Final check */ > + reg = socfpga_a10_fpga_read_stat(priv); > + > + if (((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE) == 0) || > + ((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_CONDONE_PIN) == 0) || > + ((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN) == 0)) { > + dev_dbg(&mgr->dev, > + "Timeout in final check. Status=%08xf\n", reg); > + return -ETIMEDOUT; > + } > + > + return 0; > +} > + > +static enum fpga_mgr_states socfpga_a10_fpga_state(struct fpga_manager *mgr) > +{ > + struct a10_fpga_priv *priv = mgr->priv; > + u32 reg = socfpga_a10_fpga_read_stat(priv); > + > + if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE) > + return FPGA_MGR_STATE_OPERATING; > + > + if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY) > + return FPGA_MGR_STATE_WRITE; > + > + if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_CRC_ERROR) > + return FPGA_MGR_STATE_WRITE_COMPLETE_ERR; > + > + if ((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN) == 0) > + return FPGA_MGR_STATE_RESET; > + > + return FPGA_MGR_STATE_UNKNOWN; > +} > + > +static const struct fpga_manager_ops socfpga_a10_fpga_mgr_ops = { > + .state = socfpga_a10_fpga_state, > + .write_init = socfpga_a10_fpga_write_init, > + .write = socfpga_a10_fpga_write, > + .write_complete = socfpga_a10_fpga_write_complete, > +}; > + > +static int socfpga_a10_fpga_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct a10_fpga_priv *priv; > + void __iomem *reg_base; > + struct resource *res; > + int ret; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + /* First mmio base is for register access */ > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + reg_base = devm_ioremap_resource(dev, res); > + if (IS_ERR(reg_base)) > + return PTR_ERR(reg_base); > + > + /* Second mmio base is for writing FPGA image data */ > + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); > + priv->fpga_data_addr = devm_ioremap_resource(dev, res); > + if (IS_ERR(priv->fpga_data_addr)) > + return PTR_ERR(priv->fpga_data_addr); > + > + /* regmap for register access */ > + priv->regmap = devm_regmap_init_mmio(dev, reg_base, > + &socfpga_a10_fpga_regmap_config); > + if (IS_ERR(priv->regmap)) > + return -ENODEV; > + > + priv->clk = devm_clk_get(dev, NULL); > + if (IS_ERR(priv->clk)) { > + dev_err(dev, "no clock specified\n"); > + return PTR_ERR(priv->clk); > + } > + > + ret = clk_prepare_enable(priv->clk); > + if (ret) { > + dev_err(dev, "could not enable clock\n"); > + return -EBUSY; > + } > + > + return fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager", > + &socfpga_a10_fpga_mgr_ops, priv); > +} > + > +static int socfpga_a10_fpga_remove(struct platform_device *pdev) > +{ > + struct fpga_manager *mgr = platform_get_drvdata(pdev); > + struct a10_fpga_priv *priv = mgr->priv; > + > + fpga_mgr_unregister(&pdev->dev); > + clk_disable_unprepare(priv->clk); > + > + return 0; > +} > + > +static const struct of_device_id socfpga_a10_fpga_of_match[] = { > + { .compatible = "altr,socfpga-a10-fpga-mgr", }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, socfpga_a10_fpga_of_match); > + > +static struct platform_driver socfpga_a10_fpga_driver = { > + .probe = socfpga_a10_fpga_probe, > + .remove = socfpga_a10_fpga_remove, > + .driver = { > + .name = "socfpga_a10_fpga_manager", > + .of_match_table = socfpga_a10_fpga_of_match, > + }, > +}; > + > +module_platform_driver(socfpga_a10_fpga_driver); > + > +MODULE_AUTHOR("Alan Tull "); > +MODULE_DESCRIPTION("SoCFPGA Arria10 FPGA Manager"); > +MODULE_LICENSE("GPL v2"); > -- > 2.10.1 > Looking good, Moritz