[U-Boot] [PATCH v1 1/3] arm: socfpga: stratix10: Add Stratix10 FPGA Reconfiguration Driver

Marek Vasut marex at denx.de
Thu Apr 26 12:37:48 UTC 2018


On 04/26/2018 08:12 AM, Ang, Chee Hong wrote:
> On Fri, 2018-04-20 at 05:41 +0200, Marek Vasut wrote:
>> On 04/20/2018 05:26 AM, chee.hong.ang at intel.com wrote:
>>>
>>> From: Chee Hong Ang <chee.hong.ang at intel.com>
>>>
>>> Enable FPGA reconfiguration support on Stratix10 SoC.
>>>
>>> Signed-off-by: Chee Hong Ang <chee.hong.ang at intel.com>
>>> ---
>>>  drivers/fpga/Kconfig     |  10 ++
>>>  drivers/fpga/Makefile    |   1 +
>>>  drivers/fpga/stratix10.c | 298
>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 309 insertions(+)
>>>  create mode 100644 drivers/fpga/stratix10.c
>>>
>>> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
>>> index d36c4c5..255683d 100644
>>> --- a/drivers/fpga/Kconfig
>>> +++ b/drivers/fpga/Kconfig
>>> @@ -31,6 +31,16 @@ config FPGA_CYCLON2
>>>  	  Enable FPGA driver for loading bitstream in BIT and BIN
>>> format
>>>  	  on Altera Cyclone II device.
>>>  
>>> +config FPGA_STRATIX10
>>> +	bool "Enable Altera FPGA driver for Stratix 10"
>>> +	depends on FPGA_ALTERA && TARGET_SOCFPGA_STRATIX10
>>> +	help
>>> +	  Say Y here to enable the Altera Stratix 10 FPGA specific
>>> driver
>>> +
>>> +	  This provides common functionality for Altera Stratix 10
>>> devices.
>>> +	  Enable FPGA driver for writing bitstream into Altera
>>> Stratix10
>>> +	  device.
>>> +
>>>  config FPGA_XILINX
>>>  	bool "Enable Xilinx FPGA drivers"
>>>  	select FPGA
>>> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
>>> index 08c9ff8..3c906ec 100644
>>> --- a/drivers/fpga/Makefile
>>> +++ b/drivers/fpga/Makefile
>>> @@ -19,6 +19,7 @@ obj-$(CONFIG_FPGA_ACEX1K) += ACEX1K.o
>>>  obj-$(CONFIG_FPGA_CYCLON2) += cyclon2.o
>>>  obj-$(CONFIG_FPGA_STRATIX_II) += stratixII.o
>>>  obj-$(CONFIG_FPGA_STRATIX_V) += stratixv.o
>>> +obj-$(CONFIG_FPGA_STRATIX10) += stratix10.o
>>>  obj-$(CONFIG_FPGA_SOCFPGA) += socfpga.o
>>>  obj-$(CONFIG_TARGET_SOCFPGA_GEN5) += socfpga_gen5.o
>>>  obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += socfpga_arria10.o
>>> diff --git a/drivers/fpga/stratix10.c b/drivers/fpga/stratix10.c
>>> new file mode 100644
>>> index 0000000..f0c5ace
>>> --- /dev/null
>>> +++ b/drivers/fpga/stratix10.c
>>> @@ -0,0 +1,298 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (C) 2016-2018 Intel Corporation <www.intel.com>
>>> + *
>>> + */
>>> +
>>> +#include <common.h>
>>> +#include <altera.h>
>>> +#include <asm/arch/mailbox_s10.h>
>>> +
>>> +#define RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS		60000
>>> +#define RECONFIG_STATUS_INTERVAL_DELAY_US		1000000
>>> +
>>> +static const struct mbox_cfgstat_state {
>>> +	int			err_no;
>>> +	const char		*error_name;
>>> +} mbox_cfgstat_state[] = {
>>> +	{MBOX_CFGSTAT_STATE_IDLE, "FPGA in idle mode."},
>>> +	{MBOX_CFGSTAT_STATE_CONFIG, "FPGA in config mode."},
>>> +	{MBOX_CFGSTAT_STATE_FAILACK, "Acknowledgement failed!"},
>>> +	{MBOX_CFGSTAT_STATE_ERROR_INVALID, "Invalid bitstream!"},
>>> +	{MBOX_CFGSTAT_STATE_ERROR_CORRUPT, "Corrupted
>>> bitstream!"},
>>> +	{MBOX_CFGSTAT_STATE_ERROR_AUTH, "Authentication failed!"},
>>> +	{MBOX_CFGSTAT_STATE_ERROR_CORE_IO, "I/O error!"},
>>> +	{MBOX_CFGSTAT_STATE_ERROR_HARDWARE, "Hardware error!"},
>>> +	{MBOX_CFGSTAT_STATE_ERROR_FAKE, "Fake error!"},
>>> +	{MBOX_CFGSTAT_STATE_ERROR_BOOT_INFO, "Error in boot
>>> info!"},
>>> +	{MBOX_CFGSTAT_STATE_ERROR_QSPI_ERROR, "Error in QSPI!"},
>>> +	{-ETIMEDOUT, "I/O timeout error"},
>>> +	{-1, "Unknown error!"}
>>> +};
>>> +
>>> +#define MBOX_CFGSTAT_MAX \
>>> +	  (sizeof(mbox_cfgstat_state) / sizeof(struct
>>> mbox_cfgstat_state))
>>> +
>>> +static const char *mbox_cfgstat_to_str(int err)
>>> +{
>>> +	int i;
>>> +
>>> +	for (i = 0; i < MBOX_CFGSTAT_MAX - 1; i++) {
>>> +		if (mbox_cfgstat_state[i].err_no == err)
>>> +			return mbox_cfgstat_state[i].error_name;
>>> +	}
>>> +
>>> +	return mbox_cfgstat_state[MBOX_CFGSTAT_MAX -
>>> 1].error_name;
>>> +}
>>> +
>>> +static void inc_cmd_id(u8 *id)
>>> +{
>>> +	u8 val = *id + 1;
>>> +
>>> +	if (val > 15)
>>> +		val = 1;
>> What's this function about, elaborate implementation of (i % 15) + 1
>> ?
> This function increment the 4 bits transaction ID (1-15, 0 is unused)
> used for sending mailbox command to device.
>>
>>>
>>> +	*id = val;
>>> +}
>>> +
>>> +/*
>>> + * Polling the FPGA configuration status.
>>> + * Return 0 for success, non-zero for error.
>>> + */
>>> +static int reconfig_status_polling_resp(void)
>>> +{
>>> +	u32 reconfig_status_resp_len;
>>> +	u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
>>> +	int ret;
>>> +	unsigned long start = get_timer(0);
>>> +
>>> +	while (1) {
>>> +		reconfig_status_resp_len =
>>> RECONFIG_STATUS_RESPONSE_LEN;
>>> +		ret = mbox_send_cmd(MBOX_ID_UBOOT,
>>> MBOX_RECONFIG_STATUS,
>>> +				    MBOX_CMD_DIRECT, 0, NULL, 0,
>>> +				    &reconfig_status_resp_len,
>>> +				    reconfig_status_resp);
>>> +
>>> +		if (ret) {
>>> +			puts("Failure in RECONFIG_STATUS mailbox
>>> command!\n");
>>> +			return ret;
>>> +		}
>>> +
>>> +		/* Check for any error */
>>> +		ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
>>> +		if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
>>> +			return ret;
>>> +
>>> +		/* Make sure nStatus is not 0 */
>>> +		ret =
>>> reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
>>> +		if (!(ret & RCF_PIN_STATUS_NSTATUS))
>>> +			return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
>> wait_for_bit_le32() or somesuch ?
> No, we don't read the bit status directly from register. We only need
> to test the bit of the pin status stored in memory.

Who's populating the memory ?

>>
>>>
>>> +		ret =
>>> reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
>>> +		if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
>>> +			return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
>>> +
>>> +		if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
>>> +		    (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
>>> +		    !reconfig_status_resp[RECONFIG_STATUS_STATE])
>>> +			return 0;	/* configuration success
>>> */
>>> +
>>> +		if (get_timer(start) >
>>> RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS)
>>> +			break;	/* time out */
>>> +
>>> +		puts(".");
>>> +		udelay(RECONFIG_STATUS_INTERVAL_DELAY_US);
>>> +	}
>>> +
>>> +	return -ETIMEDOUT;
>>> +}
>>> +
>>> +static u32 get_resp_hdr(u32 *r_index, u32 *w_index, u32
>>> *resp_count,
>>> +			u32 *resp_buf, u32 buf_size, u32
>>> client_id)
>>> +{
>>> +	u32 i;
>>> +	u32 mbox_hdr;
>>> +	u32 resp_len;
>>> +	u32 hdr_len;
>>> +	u32 buf[MBOX_RESP_BUFFER_SIZE];
>>> +
>>> +	if (*resp_count < buf_size) {
>>> +		u32 rcv_len_max = buf_size - *resp_count;
>>> +
>>> +		if (rcv_len_max > MBOX_RESP_BUFFER_SIZE)
>>> +			rcv_len_max = MBOX_RESP_BUFFER_SIZE;
>>> +		resp_len = mbox_rcv_resp(buf, rcv_len_max);
>>> +
>>> +		for (i = 0; i < resp_len; i++) {
>>> +			resp_buf[(*w_index)++] = buf[i];
>> Is this like a memcpy() ?
> No. This is a circular buffer, index to the memory location may wrap
> around.

Two memcpys then ?

>>>
>>> +			*w_index %= buf_size;
>>> +			(*resp_count)++;
>>> +		}
>>> +	}
>>> +
>>> +	/* No response in buffer */
>>> +	if (*resp_count == 0)
>>> +		return 0;
>>> +
>>> +	mbox_hdr = resp_buf[*r_index];
>>> +
>>> +	hdr_len = MBOX_RESP_LEN_GET(mbox_hdr);
>>> +
>>> +	/* Insufficient header length to return a mailbox header
>>> */
>>> +	if ((*resp_count - 1) < hdr_len)
>>> +		return 0;
>>> +
>>> +	*r_index += (hdr_len + 1);
>>> +	*r_index %= buf_size;
>>> +	*resp_count -= (hdr_len + 1);
>>> +
>>> +	/* Make sure response belongs to us */
>>> +	if (MBOX_RESP_CLIENT_GET(mbox_hdr) != client_id)
>>> +		return 0;
>>> +
>>> +	return mbox_hdr;
>>> +}
>>> +
>>> +static int send_reconfig_data(const void *rbf_data, size_t
>>> rbf_size,
>>> +			      u32 xfer_max, u32 buf_size_max)
>>> +{
>>> +	u32 resp_rindex = 0;
>>> +	u32 resp_windex = 0;
>>> +	u32 resp_count = 0;
>>> +	u32 response_buffer[MBOX_RESP_BUFFER_SIZE];
>>> +	u8 resp_err = 0;
>>> +	u8 cmd_id = 1;
>>> +	u32 xfer_count = 0;
>>> +	u32 xfer_pending[MBOX_RESP_BUFFER_SIZE];
>>> +	u32 args[3];
>>> +	int ret = 0;
>>> +	u32 i;
>>> +
>>> +	debug("SDM xfer_max = %d\n", xfer_max);
>>> +	debug("SDM buf_size_max = %x\n\n", buf_size_max);
>>> +
>>> +	for (i = 0; i < MBOX_RESP_BUFFER_SIZE; i++)
>>> +		xfer_pending[i] = 0;
>>> +
>>> +	while (rbf_size || xfer_count) {
>>> +		if (!resp_err && rbf_size && xfer_count <
>>> xfer_max) {
>> This function is clearly too long, split it into separate functions
> This function is not short but not too long. But let me see what sort
> of refactoring can be done to make it shorter abit.

Split it, it is too long.

>>>
>>> +			args[0] = (1 << 8);
>> That's BIT(8) .
> Will be addressed in v2 patch.
>>
>>>
>>> +			args[1] = (u32)rbf_data;
>>> +			if (rbf_size >= buf_size_max) {
>>> +				args[2] = buf_size_max;
>>> +				rbf_size -= buf_size_max;
>>> +				rbf_data += buf_size_max;
>>> +			} else {
>>> +				args[2] = (u32)rbf_size;
>>> +				rbf_size = 0;
>>> +			}
>>> +
>>> +			debug("Sending MBOX_RECONFIG_DATA...\n");
>>> +
>>> +			ret = mbox_send_cmd_only(cmd_id,
>>> MBOX_RECONFIG_DATA,
>>> +						 MBOX_CMD_INDIRECT
>>> , 3, args);
>>> +			if (ret) {
>>> +				resp_err = 1;
>>> +			} else {
>>> +				xfer_count++;
>>> +				for (i = 0; i <
>>> MBOX_RESP_BUFFER_SIZE; i++) {
>>> +					if (!xfer_pending[i]) {
>>> +						xfer_pending[i] =
>>> cmd_id;
>>> +						inc_cmd_id(&cmd_id
>>> );
>>> +						break;
>>> +					}
>>> +				}
>>> +				debug("+xfer_count = %d\n",
>>> xfer_count);
>>> +				debug("xfer ID = %d\n",
>>> xfer_pending[i]);
>>> +				debug("data offset = %08x\n",
>>> args[1]);
>>> +				debug("data size = %08x\n",
>>> args[2]);
>>> +			}
>>> +#ifndef DEBUG
>>> +			puts(".");
>> debug(".") is the same, except without the ifdef
> This is #ifndef DEBUG, we only print '.' in non-debug environment.

Oh, OK

>>>
>>> +#endif
>>> +		} else {
>>> +			u32 r_id = 0;
>>> +			u32 resp_hdr = get_resp_hdr(&resp_rindex,
>>> &resp_windex,
>>> +						    &resp_count,
>>> +						    response_buffe
>>> r,
>>> +						    MBOX_RESP_BUFF
>>> ER_SIZE,
>>> +						    MBOX_CLIENT_ID
>>> _UBOOT);
>>> +
>>> +			/* If no valid response header found */
>>> +			if (!resp_hdr)
>>> +				continue;
>>> +
>>> +			/* Expect 0 length from RECONFIG_DATA */
>>> +			if (MBOX_RESP_LEN_GET(resp_hdr))
>>> +				continue;
>>> +
>>> +			/* Check for response's status */
>>> +			if (!resp_err) {
>>> +				ret = MBOX_RESP_ERR_GET(resp_hdr);
>>> +				debug("Response error code:
>>> %08x\n", ret);
>>> +				/* Error in response */
>>> +				if (ret)
>>> +					resp_err = 1;
>>> +			}
>>> +
>>> +			/* Read the response header's ID */
>>> +			r_id = MBOX_RESP_ID_GET(resp_hdr);
>>> +			for (i = 0; i < MBOX_RESP_BUFFER_SIZE;
>>> i++) {
>>> +				if (r_id == xfer_pending[i]) {
>>> +					/* Reclaim ID */
>>> +					cmd_id =
>>> (u32)xfer_pending[i];
>>> +					xfer_pending[i] = 0;
>>> +					xfer_count--;
>>> +					break;
>>> +				}
>>> +			}
>>> +
>>> +			debug("Reclaimed xfer ID = %d\n", cmd_id);
>>> +			debug("-xfer_count = %d\n", xfer_count);
>>> +			if (resp_err && !xfer_count)
>>> +				return ret;
>>> +		}
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/*
>>> + * This is the interface used by FPGA driver.
>>> + * Return 0 for success, non-zero for error.
>>> + */
>>> +int stratix10_load(Altera_desc *desc, const void *rbf_data, size_t
>>> rbf_size)
>>> +{
>>> +	int ret;
>>> +	u32 resp_len = 2;
>>> +	u32 resp_buf[2];
>>> +
>>> +	debug("Sending MBOX_RECONFIG...\n");
>>> +	ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RECONFIG,
>>> MBOX_CMD_DIRECT, 0,
>>> +			    NULL, 0, &resp_len, resp_buf);
>>> +	if (ret) {
>>> +		puts("Failure in RECONFIG mailbox command!\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = send_reconfig_data(rbf_data, rbf_size, resp_buf[0],
>>> resp_buf[1]);
>>> +	if (ret) {
>>> +		printf("RECONFIG_DATA error: %08x, %s\n", ret,
>>> +		       mbox_cfgstat_to_str(ret));
>>> +		return ret;
>>> +	}
>>> +
>>> +	/* Make sure we don't send MBOX_RECONFIG_STATUS too fast
>>> */
>>> +	udelay(RECONFIG_STATUS_INTERVAL_DELAY_US);
>> Hum, this is iffy, is that a hardware bug ?
> Yes. The firmware running in that device is not able to response
> quickly.

And you cannot poll it in some way ?

>>>
>>> +	debug("\nPolling with MBOX_RECONFIG_STATUS...\n");
>>> +	ret = reconfig_status_polling_resp();
>>> +	if (ret) {
>>> +		printf("Error: %s\n", mbox_cfgstat_to_str(ret));
>>> +		return ret;
>>> +	}
>>> +
>>> +	puts("\nFPGA reconfiguration OK!\n");
>>> +
>>> +	return ret;
>>> +}
>>>


-- 
Best regards,
Marek Vasut


More information about the U-Boot mailing list