18c2ecf20Sopenharmony_ci/*****************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci *     Author: Xilinx, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *     This program is free software; you can redistribute it and/or modify it
68c2ecf20Sopenharmony_ci *     under the terms of the GNU General Public License as published by the
78c2ecf20Sopenharmony_ci *     Free Software Foundation; either version 2 of the License, or (at your
88c2ecf20Sopenharmony_ci *     option) any later version.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
118c2ecf20Sopenharmony_ci *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
128c2ecf20Sopenharmony_ci *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
138c2ecf20Sopenharmony_ci *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
148c2ecf20Sopenharmony_ci *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
158c2ecf20Sopenharmony_ci *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
168c2ecf20Sopenharmony_ci *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
178c2ecf20Sopenharmony_ci *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
188c2ecf20Sopenharmony_ci *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
198c2ecf20Sopenharmony_ci *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
208c2ecf20Sopenharmony_ci *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
218c2ecf20Sopenharmony_ci *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
228c2ecf20Sopenharmony_ci *     FOR A PARTICULAR PURPOSE.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *     (c) Copyright 2003-2008 Xilinx Inc.
258c2ecf20Sopenharmony_ci *     All rights reserved.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *     You should have received a copy of the GNU General Public License along
288c2ecf20Sopenharmony_ci *     with this program; if not, write to the Free Software Foundation, Inc.,
298c2ecf20Sopenharmony_ci *     675 Mass Ave, Cambridge, MA 02139, USA.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci *****************************************************************************/
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "buffer_icap.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Indicates how many bytes will fit in a buffer. (1 BRAM) */
368c2ecf20Sopenharmony_ci#define XHI_MAX_BUFFER_BYTES        2048
378c2ecf20Sopenharmony_ci#define XHI_MAX_BUFFER_INTS         (XHI_MAX_BUFFER_BYTES >> 2)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* File access and error constants */
408c2ecf20Sopenharmony_ci#define XHI_DEVICE_READ_ERROR       -1
418c2ecf20Sopenharmony_ci#define XHI_DEVICE_WRITE_ERROR      -2
428c2ecf20Sopenharmony_ci#define XHI_BUFFER_OVERFLOW_ERROR   -3
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define XHI_DEVICE_READ             0x1
458c2ecf20Sopenharmony_ci#define XHI_DEVICE_WRITE            0x0
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Constants for checking transfer status */
488c2ecf20Sopenharmony_ci#define XHI_CYCLE_DONE              0
498c2ecf20Sopenharmony_ci#define XHI_CYCLE_EXECUTING         1
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* buffer_icap register offsets */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* Size of transfer, read & write */
548c2ecf20Sopenharmony_ci#define XHI_SIZE_REG_OFFSET        0x800L
558c2ecf20Sopenharmony_ci/* offset into bram, read & write */
568c2ecf20Sopenharmony_ci#define XHI_BRAM_OFFSET_REG_OFFSET 0x804L
578c2ecf20Sopenharmony_ci/* Read not Configure, direction of transfer.  Write only */
588c2ecf20Sopenharmony_ci#define XHI_RNC_REG_OFFSET         0x808L
598c2ecf20Sopenharmony_ci/* Indicates transfer complete. Read only */
608c2ecf20Sopenharmony_ci#define XHI_STATUS_REG_OFFSET      0x80CL
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Constants for setting the RNC register */
638c2ecf20Sopenharmony_ci#define XHI_CONFIGURE              0x0UL
648c2ecf20Sopenharmony_ci#define XHI_READBACK               0x1UL
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* Constants for the Done register */
678c2ecf20Sopenharmony_ci#define XHI_NOT_FINISHED           0x0UL
688c2ecf20Sopenharmony_ci#define XHI_FINISHED               0x1UL
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define XHI_BUFFER_START 0
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/**
738c2ecf20Sopenharmony_ci * buffer_icap_get_status - Get the contents of the status register.
748c2ecf20Sopenharmony_ci * @drvdata: a pointer to the drvdata.
758c2ecf20Sopenharmony_ci *
768c2ecf20Sopenharmony_ci * The status register contains the ICAP status and the done bit.
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * D8 - cfgerr
798c2ecf20Sopenharmony_ci * D7 - dalign
808c2ecf20Sopenharmony_ci * D6 - rip
818c2ecf20Sopenharmony_ci * D5 - in_abort_l
828c2ecf20Sopenharmony_ci * D4 - Always 1
838c2ecf20Sopenharmony_ci * D3 - Always 1
848c2ecf20Sopenharmony_ci * D2 - Always 1
858c2ecf20Sopenharmony_ci * D1 - Always 1
868c2ecf20Sopenharmony_ci * D0 - Done bit
878c2ecf20Sopenharmony_ci **/
888c2ecf20Sopenharmony_ciu32 buffer_icap_get_status(struct hwicap_drvdata *drvdata)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	return in_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/**
948c2ecf20Sopenharmony_ci * buffer_icap_get_bram - Reads data from the storage buffer bram.
958c2ecf20Sopenharmony_ci * @base_address: contains the base address of the component.
968c2ecf20Sopenharmony_ci * @offset: The word offset from which the data should be read.
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci * A bram is used as a configuration memory cache.  One frame of data can
998c2ecf20Sopenharmony_ci * be stored in this "storage buffer".
1008c2ecf20Sopenharmony_ci **/
1018c2ecf20Sopenharmony_cistatic inline u32 buffer_icap_get_bram(void __iomem *base_address,
1028c2ecf20Sopenharmony_ci		u32 offset)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	return in_be32(base_address + (offset << 2));
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/**
1088c2ecf20Sopenharmony_ci * buffer_icap_busy - Return true if the icap device is busy
1098c2ecf20Sopenharmony_ci * @base_address: is the base address of the device
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * The queries the low order bit of the status register, which
1128c2ecf20Sopenharmony_ci * indicates whether the current configuration or readback operation
1138c2ecf20Sopenharmony_ci * has completed.
1148c2ecf20Sopenharmony_ci **/
1158c2ecf20Sopenharmony_cistatic inline bool buffer_icap_busy(void __iomem *base_address)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	u32 status = in_be32(base_address + XHI_STATUS_REG_OFFSET);
1188c2ecf20Sopenharmony_ci	return (status & 1) == XHI_NOT_FINISHED;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/**
1228c2ecf20Sopenharmony_ci * buffer_icap_set_size - Set the size register.
1238c2ecf20Sopenharmony_ci * @base_address: is the base address of the device
1248c2ecf20Sopenharmony_ci * @data: The size in bytes.
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * The size register holds the number of 8 bit bytes to transfer between
1278c2ecf20Sopenharmony_ci * bram and the icap (or icap to bram).
1288c2ecf20Sopenharmony_ci **/
1298c2ecf20Sopenharmony_cistatic inline void buffer_icap_set_size(void __iomem *base_address,
1308c2ecf20Sopenharmony_ci		u32 data)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	out_be32(base_address + XHI_SIZE_REG_OFFSET, data);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/**
1368c2ecf20Sopenharmony_ci * buffer_icap_set_offset - Set the bram offset register.
1378c2ecf20Sopenharmony_ci * @base_address: contains the base address of the device.
1388c2ecf20Sopenharmony_ci * @data: is the value to be written to the data register.
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * The bram offset register holds the starting bram address to transfer
1418c2ecf20Sopenharmony_ci * data from during configuration or write data to during readback.
1428c2ecf20Sopenharmony_ci **/
1438c2ecf20Sopenharmony_cistatic inline void buffer_icap_set_offset(void __iomem *base_address,
1448c2ecf20Sopenharmony_ci		u32 data)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	out_be32(base_address + XHI_BRAM_OFFSET_REG_OFFSET, data);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/**
1508c2ecf20Sopenharmony_ci * buffer_icap_set_rnc - Set the RNC (Readback not Configure) register.
1518c2ecf20Sopenharmony_ci * @base_address: contains the base address of the device.
1528c2ecf20Sopenharmony_ci * @data: is the value to be written to the data register.
1538c2ecf20Sopenharmony_ci *
1548c2ecf20Sopenharmony_ci * The RNC register determines the direction of the data transfer.  It
1558c2ecf20Sopenharmony_ci * controls whether a configuration or readback take place.  Writing to
1568c2ecf20Sopenharmony_ci * this register initiates the transfer.  A value of 1 initiates a
1578c2ecf20Sopenharmony_ci * readback while writing a value of 0 initiates a configuration.
1588c2ecf20Sopenharmony_ci **/
1598c2ecf20Sopenharmony_cistatic inline void buffer_icap_set_rnc(void __iomem *base_address,
1608c2ecf20Sopenharmony_ci		u32 data)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	out_be32(base_address + XHI_RNC_REG_OFFSET, data);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/**
1668c2ecf20Sopenharmony_ci * buffer_icap_set_bram - Write data to the storage buffer bram.
1678c2ecf20Sopenharmony_ci * @base_address: contains the base address of the component.
1688c2ecf20Sopenharmony_ci * @offset: The word offset at which the data should be written.
1698c2ecf20Sopenharmony_ci * @data: The value to be written to the bram offset.
1708c2ecf20Sopenharmony_ci *
1718c2ecf20Sopenharmony_ci * A bram is used as a configuration memory cache.  One frame of data can
1728c2ecf20Sopenharmony_ci * be stored in this "storage buffer".
1738c2ecf20Sopenharmony_ci **/
1748c2ecf20Sopenharmony_cistatic inline void buffer_icap_set_bram(void __iomem *base_address,
1758c2ecf20Sopenharmony_ci		u32 offset, u32 data)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	out_be32(base_address + (offset << 2), data);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/**
1818c2ecf20Sopenharmony_ci * buffer_icap_device_read - Transfer bytes from ICAP to the storage buffer.
1828c2ecf20Sopenharmony_ci * @drvdata: a pointer to the drvdata.
1838c2ecf20Sopenharmony_ci * @offset: The storage buffer start address.
1848c2ecf20Sopenharmony_ci * @count: The number of words (32 bit) to read from the
1858c2ecf20Sopenharmony_ci *           device (ICAP).
1868c2ecf20Sopenharmony_ci **/
1878c2ecf20Sopenharmony_cistatic int buffer_icap_device_read(struct hwicap_drvdata *drvdata,
1888c2ecf20Sopenharmony_ci		u32 offset, u32 count)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	s32 retries = 0;
1928c2ecf20Sopenharmony_ci	void __iomem *base_address = drvdata->base_address;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (buffer_icap_busy(base_address))
1958c2ecf20Sopenharmony_ci		return -EBUSY;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if ((offset + count) > XHI_MAX_BUFFER_INTS)
1988c2ecf20Sopenharmony_ci		return -EINVAL;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* setSize count*4 to get bytes. */
2018c2ecf20Sopenharmony_ci	buffer_icap_set_size(base_address, (count << 2));
2028c2ecf20Sopenharmony_ci	buffer_icap_set_offset(base_address, offset);
2038c2ecf20Sopenharmony_ci	buffer_icap_set_rnc(base_address, XHI_READBACK);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	while (buffer_icap_busy(base_address)) {
2068c2ecf20Sopenharmony_ci		retries++;
2078c2ecf20Sopenharmony_ci		if (retries > XHI_MAX_RETRIES)
2088c2ecf20Sopenharmony_ci			return -EBUSY;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci	return 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci};
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/**
2158c2ecf20Sopenharmony_ci * buffer_icap_device_write - Transfer bytes from ICAP to the storage buffer.
2168c2ecf20Sopenharmony_ci * @drvdata: a pointer to the drvdata.
2178c2ecf20Sopenharmony_ci * @offset: The storage buffer start address.
2188c2ecf20Sopenharmony_ci * @count: The number of words (32 bit) to read from the
2198c2ecf20Sopenharmony_ci *           device (ICAP).
2208c2ecf20Sopenharmony_ci **/
2218c2ecf20Sopenharmony_cistatic int buffer_icap_device_write(struct hwicap_drvdata *drvdata,
2228c2ecf20Sopenharmony_ci		u32 offset, u32 count)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	s32 retries = 0;
2268c2ecf20Sopenharmony_ci	void __iomem *base_address = drvdata->base_address;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (buffer_icap_busy(base_address))
2298c2ecf20Sopenharmony_ci		return -EBUSY;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if ((offset + count) > XHI_MAX_BUFFER_INTS)
2328c2ecf20Sopenharmony_ci		return -EINVAL;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* setSize count*4 to get bytes. */
2358c2ecf20Sopenharmony_ci	buffer_icap_set_size(base_address, count << 2);
2368c2ecf20Sopenharmony_ci	buffer_icap_set_offset(base_address, offset);
2378c2ecf20Sopenharmony_ci	buffer_icap_set_rnc(base_address, XHI_CONFIGURE);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	while (buffer_icap_busy(base_address)) {
2408c2ecf20Sopenharmony_ci		retries++;
2418c2ecf20Sopenharmony_ci		if (retries > XHI_MAX_RETRIES)
2428c2ecf20Sopenharmony_ci			return -EBUSY;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/**
2498c2ecf20Sopenharmony_ci * buffer_icap_reset - Reset the logic of the icap device.
2508c2ecf20Sopenharmony_ci * @drvdata: a pointer to the drvdata.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci * Writing to the status register resets the ICAP logic in an internal
2538c2ecf20Sopenharmony_ci * version of the core.  For the version of the core published in EDK,
2548c2ecf20Sopenharmony_ci * this is a noop.
2558c2ecf20Sopenharmony_ci **/
2568c2ecf20Sopenharmony_civoid buffer_icap_reset(struct hwicap_drvdata *drvdata)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci    out_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET, 0xFEFE);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/**
2628c2ecf20Sopenharmony_ci * buffer_icap_set_configuration - Load a partial bitstream from system memory.
2638c2ecf20Sopenharmony_ci * @drvdata: a pointer to the drvdata.
2648c2ecf20Sopenharmony_ci * @data: Kernel address of the partial bitstream.
2658c2ecf20Sopenharmony_ci * @size: the size of the partial bitstream in 32 bit words.
2668c2ecf20Sopenharmony_ci **/
2678c2ecf20Sopenharmony_ciint buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
2688c2ecf20Sopenharmony_ci			     u32 size)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	int status;
2718c2ecf20Sopenharmony_ci	s32 buffer_count = 0;
2728c2ecf20Sopenharmony_ci	bool dirty = false;
2738c2ecf20Sopenharmony_ci	u32 i;
2748c2ecf20Sopenharmony_ci	void __iomem *base_address = drvdata->base_address;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Loop through all the data */
2778c2ecf20Sopenharmony_ci	for (i = 0, buffer_count = 0; i < size; i++) {
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		/* Copy data to bram */
2808c2ecf20Sopenharmony_ci		buffer_icap_set_bram(base_address, buffer_count, data[i]);
2818c2ecf20Sopenharmony_ci		dirty = true;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		if (buffer_count < XHI_MAX_BUFFER_INTS - 1) {
2848c2ecf20Sopenharmony_ci			buffer_count++;
2858c2ecf20Sopenharmony_ci			continue;
2868c2ecf20Sopenharmony_ci		}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		/* Write data to ICAP */
2898c2ecf20Sopenharmony_ci		status = buffer_icap_device_write(
2908c2ecf20Sopenharmony_ci				drvdata,
2918c2ecf20Sopenharmony_ci				XHI_BUFFER_START,
2928c2ecf20Sopenharmony_ci				XHI_MAX_BUFFER_INTS);
2938c2ecf20Sopenharmony_ci		if (status != 0) {
2948c2ecf20Sopenharmony_ci			/* abort. */
2958c2ecf20Sopenharmony_ci			buffer_icap_reset(drvdata);
2968c2ecf20Sopenharmony_ci			return status;
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		buffer_count = 0;
3008c2ecf20Sopenharmony_ci		dirty = false;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/* Write unwritten data to ICAP */
3048c2ecf20Sopenharmony_ci	if (dirty) {
3058c2ecf20Sopenharmony_ci		/* Write data to ICAP */
3068c2ecf20Sopenharmony_ci		status = buffer_icap_device_write(drvdata, XHI_BUFFER_START,
3078c2ecf20Sopenharmony_ci					     buffer_count);
3088c2ecf20Sopenharmony_ci		if (status != 0) {
3098c2ecf20Sopenharmony_ci			/* abort. */
3108c2ecf20Sopenharmony_ci			buffer_icap_reset(drvdata);
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci		return status;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/**
3198c2ecf20Sopenharmony_ci * buffer_icap_get_configuration - Read configuration data from the device.
3208c2ecf20Sopenharmony_ci * @drvdata: a pointer to the drvdata.
3218c2ecf20Sopenharmony_ci * @data: Address of the data representing the partial bitstream
3228c2ecf20Sopenharmony_ci * @size: the size of the partial bitstream in 32 bit words.
3238c2ecf20Sopenharmony_ci **/
3248c2ecf20Sopenharmony_ciint buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data,
3258c2ecf20Sopenharmony_ci			     u32 size)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	int status;
3288c2ecf20Sopenharmony_ci	s32 buffer_count = 0;
3298c2ecf20Sopenharmony_ci	u32 i;
3308c2ecf20Sopenharmony_ci	void __iomem *base_address = drvdata->base_address;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Loop through all the data */
3338c2ecf20Sopenharmony_ci	for (i = 0, buffer_count = XHI_MAX_BUFFER_INTS; i < size; i++) {
3348c2ecf20Sopenharmony_ci		if (buffer_count == XHI_MAX_BUFFER_INTS) {
3358c2ecf20Sopenharmony_ci			u32 words_remaining = size - i;
3368c2ecf20Sopenharmony_ci			u32 words_to_read =
3378c2ecf20Sopenharmony_ci				words_remaining <
3388c2ecf20Sopenharmony_ci				XHI_MAX_BUFFER_INTS ? words_remaining :
3398c2ecf20Sopenharmony_ci				XHI_MAX_BUFFER_INTS;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci			/* Read data from ICAP */
3428c2ecf20Sopenharmony_ci			status = buffer_icap_device_read(
3438c2ecf20Sopenharmony_ci					drvdata,
3448c2ecf20Sopenharmony_ci					XHI_BUFFER_START,
3458c2ecf20Sopenharmony_ci					words_to_read);
3468c2ecf20Sopenharmony_ci			if (status != 0) {
3478c2ecf20Sopenharmony_ci				/* abort. */
3488c2ecf20Sopenharmony_ci				buffer_icap_reset(drvdata);
3498c2ecf20Sopenharmony_ci				return status;
3508c2ecf20Sopenharmony_ci			}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci			buffer_count = 0;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		/* Copy data from bram */
3568c2ecf20Sopenharmony_ci		data[i] = buffer_icap_get_bram(base_address, buffer_count);
3578c2ecf20Sopenharmony_ci		buffer_count++;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return 0;
3618c2ecf20Sopenharmony_ci};
362