cregit-Linux how code gets into the kernel

Release 4.17 drivers/fpga/socfpga-a10.c

Directory: drivers/fpga
/*
 * 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 <http://www.gnu.org/licenses/>.
 */

#include <linux/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/regmap.h>


#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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull42100.00%1100.00%
Total42100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull45100.00%1100.00%
Total45100.00%1100.00%

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); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull31100.00%1100.00%
Total31100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull71100.00%1100.00%
Total71100.00%1100.00%

#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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull42100.00%1100.00%
Total42100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull41100.00%1100.00%
Total41100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull60100.00%1100.00%
Total60100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull120100.00%1100.00%
Total120100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull29100.00%1100.00%
Total29100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull61100.00%1100.00%
Total61100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull61100.00%1100.00%
Total61100.00%1100.00%

/* 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); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull264100.00%1100.00%
Total264100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull162100.00%1100.00%
Total162100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull172100.00%1100.00%
Total172100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull72100.00%1100.00%
Total72100.00%1100.00%

static const struct fpga_manager_ops socfpga_a10_fpga_mgr_ops = { .initial_header_size = (RBF_DECOMPRESS_OFFSET + 1) * 4, .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; } ret = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager", &socfpga_a10_fpga_mgr_ops, priv); if (ret) { clk_disable_unprepare(priv->clk); return ret; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull24091.95%150.00%
Alexey Khoroshilov218.05%150.00%
Total261100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Tull48100.00%1100.00%
Total48100.00%1100.00%

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 <atull@opensource.altera.com>"); MODULE_DESCRIPTION("SoCFPGA Arria10 FPGA Manager"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Alan Tull192098.36%133.33%
Alexey Khoroshilov211.08%133.33%
Jason Gunthorpe110.56%133.33%
Total1952100.00%3100.00%
Directory: drivers/fpga
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.