18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * FPGA Manager Driver for Altera Arria10 SoCFPGA
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Altera Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/device.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/fpga/fpga-mgr.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define A10_FPGAMGR_DCLKCNT_OFST				0x08
178c2ecf20Sopenharmony_ci#define A10_FPGAMGR_DCLKSTAT_OFST				0x0c
188c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_00_OFST				0x70
198c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_01_OFST				0x74
208c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_02_OFST				0x78
218c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_OFST				0x80
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define A10_FPGAMGR_DCLKSTAT_DCLKDONE				BIT(0)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NCONFIG		BIT(0)
268c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NSTATUS		BIT(1)
278c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_CONDONE		BIT(2)
288c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NCONFIG			BIT(8)
298c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NSTATUS_OE		BIT(16)
308c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_CONDONE_OE		BIT(24)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG		BIT(0)
338c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST		BIT(16)
348c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE			BIT(24)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL			BIT(0)
378c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_MASK		(BIT(16) | BIT(17))
388c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_SHIFT			16
398c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH			BIT(24)
408c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH_SHIFT		24
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_CRC_ERROR			BIT(0)
438c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_EARLY_USERMODE		BIT(1)
448c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE			BIT(2)
458c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN			BIT(4)
468c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_CONDONE_PIN			BIT(6)
478c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY			BIT(9)
488c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_DONE			BIT(10)
498c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR			BIT(11)
508c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_NCONFIG_PIN			BIT(12)
518c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_MASK	(BIT(16) | BIT(17) | BIT(18))
528c2ecf20Sopenharmony_ci#define A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_SHIFT		        16
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* FPGA CD Ratio Value */
558c2ecf20Sopenharmony_ci#define CDRATIO_x1						0x0
568c2ecf20Sopenharmony_ci#define CDRATIO_x2						0x1
578c2ecf20Sopenharmony_ci#define CDRATIO_x4						0x2
588c2ecf20Sopenharmony_ci#define CDRATIO_x8						0x3
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* Configuration width 16/32 bit */
618c2ecf20Sopenharmony_ci#define CFGWDTH_32						1
628c2ecf20Sopenharmony_ci#define CFGWDTH_16						0
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * struct a10_fpga_priv - private data for fpga manager
668c2ecf20Sopenharmony_ci * @regmap: regmap for register access
678c2ecf20Sopenharmony_ci * @fpga_data_addr: iomap for single address data register to FPGA
688c2ecf20Sopenharmony_ci * @clk: clock
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistruct a10_fpga_priv {
718c2ecf20Sopenharmony_ci	struct regmap *regmap;
728c2ecf20Sopenharmony_ci	void __iomem *fpga_data_addr;
738c2ecf20Sopenharmony_ci	struct clk *clk;
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic bool socfpga_a10_fpga_writeable_reg(struct device *dev, unsigned int reg)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	switch (reg) {
798c2ecf20Sopenharmony_ci	case A10_FPGAMGR_DCLKCNT_OFST:
808c2ecf20Sopenharmony_ci	case A10_FPGAMGR_DCLKSTAT_OFST:
818c2ecf20Sopenharmony_ci	case A10_FPGAMGR_IMGCFG_CTL_00_OFST:
828c2ecf20Sopenharmony_ci	case A10_FPGAMGR_IMGCFG_CTL_01_OFST:
838c2ecf20Sopenharmony_ci	case A10_FPGAMGR_IMGCFG_CTL_02_OFST:
848c2ecf20Sopenharmony_ci		return true;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	return false;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic bool socfpga_a10_fpga_readable_reg(struct device *dev, unsigned int reg)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	switch (reg) {
928c2ecf20Sopenharmony_ci	case A10_FPGAMGR_DCLKCNT_OFST:
938c2ecf20Sopenharmony_ci	case A10_FPGAMGR_DCLKSTAT_OFST:
948c2ecf20Sopenharmony_ci	case A10_FPGAMGR_IMGCFG_CTL_00_OFST:
958c2ecf20Sopenharmony_ci	case A10_FPGAMGR_IMGCFG_CTL_01_OFST:
968c2ecf20Sopenharmony_ci	case A10_FPGAMGR_IMGCFG_CTL_02_OFST:
978c2ecf20Sopenharmony_ci	case A10_FPGAMGR_IMGCFG_STAT_OFST:
988c2ecf20Sopenharmony_ci		return true;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci	return false;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic const struct regmap_config socfpga_a10_fpga_regmap_config = {
1048c2ecf20Sopenharmony_ci	.reg_bits = 32,
1058c2ecf20Sopenharmony_ci	.reg_stride = 4,
1068c2ecf20Sopenharmony_ci	.val_bits = 32,
1078c2ecf20Sopenharmony_ci	.writeable_reg = socfpga_a10_fpga_writeable_reg,
1088c2ecf20Sopenharmony_ci	.readable_reg = socfpga_a10_fpga_readable_reg,
1098c2ecf20Sopenharmony_ci	.max_register = A10_FPGAMGR_IMGCFG_STAT_OFST,
1108c2ecf20Sopenharmony_ci	.cache_type = REGCACHE_NONE,
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/*
1148c2ecf20Sopenharmony_ci * from the register map description of cdratio in imgcfg_ctrl_02:
1158c2ecf20Sopenharmony_ci *  Normal Configuration    : 32bit Passive Parallel
1168c2ecf20Sopenharmony_ci *  Partial Reconfiguration : 16bit Passive Parallel
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_cistatic void socfpga_a10_fpga_set_cfg_width(struct a10_fpga_priv *priv,
1198c2ecf20Sopenharmony_ci					   int width)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	width <<= A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH_SHIFT;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST,
1248c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH, width);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic void socfpga_a10_fpga_generate_dclks(struct a10_fpga_priv *priv,
1288c2ecf20Sopenharmony_ci					    u32 count)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	u32 val;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* Clear any existing DONE status. */
1338c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST,
1348c2ecf20Sopenharmony_ci		     A10_FPGAMGR_DCLKSTAT_DCLKDONE);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Issue the DCLK regmap. */
1378c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, A10_FPGAMGR_DCLKCNT_OFST, count);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* wait till the dclkcnt done */
1408c2ecf20Sopenharmony_ci	regmap_read_poll_timeout(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST, val,
1418c2ecf20Sopenharmony_ci				 val, 1, 100);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Clear DONE status. */
1448c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST,
1458c2ecf20Sopenharmony_ci		     A10_FPGAMGR_DCLKSTAT_DCLKDONE);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci#define RBF_ENCRYPTION_MODE_OFFSET		69
1498c2ecf20Sopenharmony_ci#define RBF_DECOMPRESS_OFFSET			229
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_encrypted(u32 *buf32, size_t buf32_size)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	if (buf32_size < RBF_ENCRYPTION_MODE_OFFSET + 1)
1548c2ecf20Sopenharmony_ci		return -EINVAL;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* Is the bitstream encrypted? */
1578c2ecf20Sopenharmony_ci	return ((buf32[RBF_ENCRYPTION_MODE_OFFSET] >> 2) & 3) != 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_compressed(u32 *buf32, size_t buf32_size)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	if (buf32_size < RBF_DECOMPRESS_OFFSET + 1)
1638c2ecf20Sopenharmony_ci		return -EINVAL;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Is the bitstream compressed? */
1668c2ecf20Sopenharmony_ci	return !((buf32[RBF_DECOMPRESS_OFFSET] >> 1) & 1);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic unsigned int socfpga_a10_fpga_get_cd_ratio(unsigned int cfg_width,
1708c2ecf20Sopenharmony_ci						  bool encrypt, bool compress)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	unsigned int cd_ratio;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/*
1758c2ecf20Sopenharmony_ci	 * cd ratio is dependent on cfg width and whether the bitstream
1768c2ecf20Sopenharmony_ci	 * is encrypted and/or compressed.
1778c2ecf20Sopenharmony_ci	 *
1788c2ecf20Sopenharmony_ci	 * | width | encr. | compr. | cd ratio |
1798c2ecf20Sopenharmony_ci	 * |  16   |   0   |   0    |     1    |
1808c2ecf20Sopenharmony_ci	 * |  16   |   0   |   1    |     4    |
1818c2ecf20Sopenharmony_ci	 * |  16   |   1   |   0    |     2    |
1828c2ecf20Sopenharmony_ci	 * |  16   |   1   |   1    |     4    |
1838c2ecf20Sopenharmony_ci	 * |  32   |   0   |   0    |     1    |
1848c2ecf20Sopenharmony_ci	 * |  32   |   0   |   1    |     8    |
1858c2ecf20Sopenharmony_ci	 * |  32   |   1   |   0    |     4    |
1868c2ecf20Sopenharmony_ci	 * |  32   |   1   |   1    |     8    |
1878c2ecf20Sopenharmony_ci	 */
1888c2ecf20Sopenharmony_ci	if (!compress && !encrypt)
1898c2ecf20Sopenharmony_ci		return CDRATIO_x1;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (compress)
1928c2ecf20Sopenharmony_ci		cd_ratio = CDRATIO_x4;
1938c2ecf20Sopenharmony_ci	else
1948c2ecf20Sopenharmony_ci		cd_ratio = CDRATIO_x2;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* If 32 bit, double the cd ratio by incrementing the field  */
1978c2ecf20Sopenharmony_ci	if (cfg_width == CFGWDTH_32)
1988c2ecf20Sopenharmony_ci		cd_ratio += 1;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return cd_ratio;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_set_cdratio(struct fpga_manager *mgr,
2048c2ecf20Sopenharmony_ci					unsigned int cfg_width,
2058c2ecf20Sopenharmony_ci					const char *buf, size_t count)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct a10_fpga_priv *priv = mgr->priv;
2088c2ecf20Sopenharmony_ci	unsigned int cd_ratio;
2098c2ecf20Sopenharmony_ci	int encrypt, compress;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	encrypt = socfpga_a10_fpga_encrypted((u32 *)buf, count / 4);
2128c2ecf20Sopenharmony_ci	if (encrypt < 0)
2138c2ecf20Sopenharmony_ci		return -EINVAL;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	compress = socfpga_a10_fpga_compressed((u32 *)buf, count / 4);
2168c2ecf20Sopenharmony_ci	if (compress < 0)
2178c2ecf20Sopenharmony_ci		return -EINVAL;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	cd_ratio = socfpga_a10_fpga_get_cd_ratio(cfg_width, encrypt, compress);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST,
2228c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_MASK,
2238c2ecf20Sopenharmony_ci			   cd_ratio << A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_SHIFT);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic u32 socfpga_a10_fpga_read_stat(struct a10_fpga_priv *priv)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	u32 val;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, A10_FPGAMGR_IMGCFG_STAT_OFST, &val);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return val;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_wait_for_pr_ready(struct a10_fpga_priv *priv)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	u32 reg, i;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	for (i = 0; i < 10 ; i++) {
2428c2ecf20Sopenharmony_ci		reg = socfpga_a10_fpga_read_stat(priv);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR)
2458c2ecf20Sopenharmony_ci			return -EINVAL;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY)
2488c2ecf20Sopenharmony_ci			return 0;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_wait_for_pr_done(struct a10_fpga_priv *priv)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	u32 reg, i;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	for (i = 0; i < 10 ; i++) {
2598c2ecf20Sopenharmony_ci		reg = socfpga_a10_fpga_read_stat(priv);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR)
2628c2ecf20Sopenharmony_ci			return -EINVAL;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_DONE)
2658c2ecf20Sopenharmony_ci			return 0;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/* Start the FPGA programming by initialize the FPGA Manager */
2728c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_write_init(struct fpga_manager *mgr,
2738c2ecf20Sopenharmony_ci				       struct fpga_image_info *info,
2748c2ecf20Sopenharmony_ci				       const char *buf, size_t count)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct a10_fpga_priv *priv = mgr->priv;
2778c2ecf20Sopenharmony_ci	unsigned int cfg_width;
2788c2ecf20Sopenharmony_ci	u32 msel, stat, mask;
2798c2ecf20Sopenharmony_ci	int ret;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (info->flags & FPGA_MGR_PARTIAL_RECONFIG)
2828c2ecf20Sopenharmony_ci		cfg_width = CFGWDTH_16;
2838c2ecf20Sopenharmony_ci	else
2848c2ecf20Sopenharmony_ci		return -EINVAL;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* Check for passive parallel (msel == 000 or 001) */
2878c2ecf20Sopenharmony_ci	msel = socfpga_a10_fpga_read_stat(priv);
2888c2ecf20Sopenharmony_ci	msel &= A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_MASK;
2898c2ecf20Sopenharmony_ci	msel >>= A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_SHIFT;
2908c2ecf20Sopenharmony_ci	if ((msel != 0) && (msel != 1)) {
2918c2ecf20Sopenharmony_ci		dev_dbg(&mgr->dev, "Fail: invalid msel=%d\n", msel);
2928c2ecf20Sopenharmony_ci		return -EINVAL;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* Make sure no external devices are interfering */
2968c2ecf20Sopenharmony_ci	stat = socfpga_a10_fpga_read_stat(priv);
2978c2ecf20Sopenharmony_ci	mask = A10_FPGAMGR_IMGCFG_STAT_F2S_NCONFIG_PIN |
2988c2ecf20Sopenharmony_ci	       A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN;
2998c2ecf20Sopenharmony_ci	if ((stat & mask) != mask)
3008c2ecf20Sopenharmony_ci		return -EINVAL;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Set cfg width */
3038c2ecf20Sopenharmony_ci	socfpga_a10_fpga_set_cfg_width(priv, cfg_width);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* Determine cd ratio from bitstream header and set cd ratio */
3068c2ecf20Sopenharmony_ci	ret = socfpga_a10_fpga_set_cdratio(mgr, cfg_width, buf, count);
3078c2ecf20Sopenharmony_ci	if (ret)
3088c2ecf20Sopenharmony_ci		return ret;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/*
3118c2ecf20Sopenharmony_ci	 * Clear s2f_nce to enable chip select.  Leave pr_request
3128c2ecf20Sopenharmony_ci	 * unasserted and override disabled.
3138c2ecf20Sopenharmony_ci	 */
3148c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
3158c2ecf20Sopenharmony_ci		     A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* Set cfg_ctrl to enable s2f dclk and data */
3188c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST,
3198c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL,
3208c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/*
3238c2ecf20Sopenharmony_ci	 * Disable overrides not needed for pr.
3248c2ecf20Sopenharmony_ci	 * s2f_config==1 leaves reset deasseted.
3258c2ecf20Sopenharmony_ci	 */
3268c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_00_OFST,
3278c2ecf20Sopenharmony_ci		     A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NCONFIG |
3288c2ecf20Sopenharmony_ci		     A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NSTATUS |
3298c2ecf20Sopenharmony_ci		     A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_CONDONE |
3308c2ecf20Sopenharmony_ci		     A10_FPGAMGR_IMGCFG_CTL_00_S2F_NCONFIG);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Enable override for data, dclk, nce, and pr_request to CSS */
3338c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
3348c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG, 0);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Send some clocks to clear out any errors */
3378c2ecf20Sopenharmony_ci	socfpga_a10_fpga_generate_dclks(priv, 256);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* Assert pr_request */
3408c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
3418c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST,
3428c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* Provide 2048 DCLKs before starting the config data streaming. */
3458c2ecf20Sopenharmony_ci	socfpga_a10_fpga_generate_dclks(priv, 0x7ff);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Wait for pr_ready */
3488c2ecf20Sopenharmony_ci	return socfpga_a10_fpga_wait_for_pr_ready(priv);
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci/*
3528c2ecf20Sopenharmony_ci * write data to the FPGA data register
3538c2ecf20Sopenharmony_ci */
3548c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_write(struct fpga_manager *mgr, const char *buf,
3558c2ecf20Sopenharmony_ci				  size_t count)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct a10_fpga_priv *priv = mgr->priv;
3588c2ecf20Sopenharmony_ci	u32 *buffer_32 = (u32 *)buf;
3598c2ecf20Sopenharmony_ci	size_t i = 0;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (count <= 0)
3628c2ecf20Sopenharmony_ci		return -EINVAL;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* Write out the complete 32-bit chunks */
3658c2ecf20Sopenharmony_ci	while (count >= sizeof(u32)) {
3668c2ecf20Sopenharmony_ci		writel(buffer_32[i++], priv->fpga_data_addr);
3678c2ecf20Sopenharmony_ci		count -= sizeof(u32);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* Write out remaining non 32-bit chunks */
3718c2ecf20Sopenharmony_ci	switch (count) {
3728c2ecf20Sopenharmony_ci	case 3:
3738c2ecf20Sopenharmony_ci		writel(buffer_32[i++] & 0x00ffffff, priv->fpga_data_addr);
3748c2ecf20Sopenharmony_ci		break;
3758c2ecf20Sopenharmony_ci	case 2:
3768c2ecf20Sopenharmony_ci		writel(buffer_32[i++] & 0x0000ffff, priv->fpga_data_addr);
3778c2ecf20Sopenharmony_ci		break;
3788c2ecf20Sopenharmony_ci	case 1:
3798c2ecf20Sopenharmony_ci		writel(buffer_32[i++] & 0x000000ff, priv->fpga_data_addr);
3808c2ecf20Sopenharmony_ci		break;
3818c2ecf20Sopenharmony_ci	case 0:
3828c2ecf20Sopenharmony_ci		break;
3838c2ecf20Sopenharmony_ci	default:
3848c2ecf20Sopenharmony_ci		/* This will never happen */
3858c2ecf20Sopenharmony_ci		return -EFAULT;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_write_complete(struct fpga_manager *mgr,
3928c2ecf20Sopenharmony_ci					   struct fpga_image_info *info)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct a10_fpga_priv *priv = mgr->priv;
3958c2ecf20Sopenharmony_ci	u32 reg;
3968c2ecf20Sopenharmony_ci	int ret;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* Wait for pr_done */
3998c2ecf20Sopenharmony_ci	ret = socfpga_a10_fpga_wait_for_pr_done(priv);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* Clear pr_request */
4028c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
4038c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST, 0);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/* Send some clocks to clear out any errors */
4068c2ecf20Sopenharmony_ci	socfpga_a10_fpga_generate_dclks(priv, 256);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* Disable s2f dclk and data */
4098c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST,
4108c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL, 0);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* Deassert chip select */
4138c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
4148c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE,
4158c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* Disable data, dclk, nce, and pr_request override to CSS */
4188c2ecf20Sopenharmony_ci	regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
4198c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG,
4208c2ecf20Sopenharmony_ci			   A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* Return any errors regarding pr_done or pr_error */
4238c2ecf20Sopenharmony_ci	if (ret)
4248c2ecf20Sopenharmony_ci		return ret;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* Final check */
4278c2ecf20Sopenharmony_ci	reg = socfpga_a10_fpga_read_stat(priv);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE) == 0) ||
4308c2ecf20Sopenharmony_ci	    ((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_CONDONE_PIN) == 0) ||
4318c2ecf20Sopenharmony_ci	    ((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN) == 0)) {
4328c2ecf20Sopenharmony_ci		dev_dbg(&mgr->dev,
4338c2ecf20Sopenharmony_ci			"Timeout in final check. Status=%08xf\n", reg);
4348c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return 0;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic enum fpga_mgr_states socfpga_a10_fpga_state(struct fpga_manager *mgr)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct a10_fpga_priv *priv = mgr->priv;
4438c2ecf20Sopenharmony_ci	u32 reg = socfpga_a10_fpga_read_stat(priv);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE)
4468c2ecf20Sopenharmony_ci		return FPGA_MGR_STATE_OPERATING;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY)
4498c2ecf20Sopenharmony_ci		return FPGA_MGR_STATE_WRITE;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_CRC_ERROR)
4528c2ecf20Sopenharmony_ci		return FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if ((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN) == 0)
4558c2ecf20Sopenharmony_ci		return FPGA_MGR_STATE_RESET;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return FPGA_MGR_STATE_UNKNOWN;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic const struct fpga_manager_ops socfpga_a10_fpga_mgr_ops = {
4618c2ecf20Sopenharmony_ci	.initial_header_size = (RBF_DECOMPRESS_OFFSET + 1) * 4,
4628c2ecf20Sopenharmony_ci	.state = socfpga_a10_fpga_state,
4638c2ecf20Sopenharmony_ci	.write_init = socfpga_a10_fpga_write_init,
4648c2ecf20Sopenharmony_ci	.write = socfpga_a10_fpga_write,
4658c2ecf20Sopenharmony_ci	.write_complete = socfpga_a10_fpga_write_complete,
4668c2ecf20Sopenharmony_ci};
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_probe(struct platform_device *pdev)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
4718c2ecf20Sopenharmony_ci	struct a10_fpga_priv *priv;
4728c2ecf20Sopenharmony_ci	void __iomem *reg_base;
4738c2ecf20Sopenharmony_ci	struct fpga_manager *mgr;
4748c2ecf20Sopenharmony_ci	struct resource *res;
4758c2ecf20Sopenharmony_ci	int ret;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
4788c2ecf20Sopenharmony_ci	if (!priv)
4798c2ecf20Sopenharmony_ci		return -ENOMEM;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* First mmio base is for register access */
4828c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4838c2ecf20Sopenharmony_ci	reg_base = devm_ioremap_resource(dev, res);
4848c2ecf20Sopenharmony_ci	if (IS_ERR(reg_base))
4858c2ecf20Sopenharmony_ci		return PTR_ERR(reg_base);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/* Second mmio base is for writing FPGA image data */
4888c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
4898c2ecf20Sopenharmony_ci	priv->fpga_data_addr = devm_ioremap_resource(dev, res);
4908c2ecf20Sopenharmony_ci	if (IS_ERR(priv->fpga_data_addr))
4918c2ecf20Sopenharmony_ci		return PTR_ERR(priv->fpga_data_addr);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* regmap for register access */
4948c2ecf20Sopenharmony_ci	priv->regmap = devm_regmap_init_mmio(dev, reg_base,
4958c2ecf20Sopenharmony_ci					     &socfpga_a10_fpga_regmap_config);
4968c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regmap))
4978c2ecf20Sopenharmony_ci		return -ENODEV;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	priv->clk = devm_clk_get(dev, NULL);
5008c2ecf20Sopenharmony_ci	if (IS_ERR(priv->clk)) {
5018c2ecf20Sopenharmony_ci		dev_err(dev, "no clock specified\n");
5028c2ecf20Sopenharmony_ci		return PTR_ERR(priv->clk);
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(priv->clk);
5068c2ecf20Sopenharmony_ci	if (ret) {
5078c2ecf20Sopenharmony_ci		dev_err(dev, "could not enable clock\n");
5088c2ecf20Sopenharmony_ci		return -EBUSY;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
5128c2ecf20Sopenharmony_ci				   &socfpga_a10_fpga_mgr_ops, priv);
5138c2ecf20Sopenharmony_ci	if (!mgr)
5148c2ecf20Sopenharmony_ci		return -ENOMEM;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, mgr);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	ret = fpga_mgr_register(mgr);
5198c2ecf20Sopenharmony_ci	if (ret) {
5208c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->clk);
5218c2ecf20Sopenharmony_ci		return ret;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return 0;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic int socfpga_a10_fpga_remove(struct platform_device *pdev)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	struct fpga_manager *mgr = platform_get_drvdata(pdev);
5308c2ecf20Sopenharmony_ci	struct a10_fpga_priv *priv = mgr->priv;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	fpga_mgr_unregister(mgr);
5338c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	return 0;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic const struct of_device_id socfpga_a10_fpga_of_match[] = {
5398c2ecf20Sopenharmony_ci	{ .compatible = "altr,socfpga-a10-fpga-mgr", },
5408c2ecf20Sopenharmony_ci	{},
5418c2ecf20Sopenharmony_ci};
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, socfpga_a10_fpga_of_match);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic struct platform_driver socfpga_a10_fpga_driver = {
5468c2ecf20Sopenharmony_ci	.probe = socfpga_a10_fpga_probe,
5478c2ecf20Sopenharmony_ci	.remove = socfpga_a10_fpga_remove,
5488c2ecf20Sopenharmony_ci	.driver = {
5498c2ecf20Sopenharmony_ci		.name	= "socfpga_a10_fpga_manager",
5508c2ecf20Sopenharmony_ci		.of_match_table = socfpga_a10_fpga_of_match,
5518c2ecf20Sopenharmony_ci	},
5528c2ecf20Sopenharmony_ci};
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cimodule_platform_driver(socfpga_a10_fpga_driver);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
5578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SoCFPGA Arria10 FPGA Manager");
5588c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
559