aboutsummaryrefslogtreecommitdiff
path: root/drivers/char/xilinx_hwicap/buffer_icap.c
blob: 53c3882e49812803633ac49f6b56c1ba9311e773 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
/*****************************************************************************
 *
 *     Author: Xilinx, Inc.
 *
 *     This program is free software; you can redistribute it and/or modify it
 *     under the terms of the GNU General Public License as published by the
 *     Free Software Foundation; either version 2 of the License, or (at your
 *     option) any later version.
 *
 *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
 *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
 *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
 *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
 *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
 *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
 *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
 *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
 *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
 *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
 *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
 *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *     FOR A PARTICULAR PURPOSE.
 *
 *     (c) Copyright 2003-2008 Xilinx Inc.
 *     All rights reserved.
 *
 *     You should have received a copy of the GNU General Public License along
 *     with this program; if not, write to the Free Software Foundation, Inc.,
 *     675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *****************************************************************************/

#include "buffer_icap.h"

/* Indicates how many bytes will fit in a buffer. (1 BRAM) */
#define XHI_MAX_BUFFER_BYTES        2048
#define XHI_MAX_BUFFER_INTS         (XHI_MAX_BUFFER_BYTES >> 2)

/* File access and error constants */
#define XHI_DEVICE_READ_ERROR       -1
#define XHI_DEVICE_WRITE_ERROR      -2
#define XHI_BUFFER_OVERFLOW_ERROR   -3

#define XHI_DEVICE_READ             0x1
#define XHI_DEVICE_WRITE            0x0

/* Constants for checking transfer status */
#define XHI_CYCLE_DONE              0
#define XHI_CYCLE_EXECUTING         1

/* buffer_icap register offsets */

/* Size of transfer, read & write */
#define XHI_SIZE_REG_OFFSET        0x800L
/* offset into bram, read & write */
#define XHI_BRAM_OFFSET_REG_OFFSET 0x804L
/* Read not Configure, direction of transfer.  Write only */
#define XHI_RNC_REG_OFFSET         0x808L
/* Indicates transfer complete. Read only */
#define XHI_STATUS_REG_OFFSET      0x80CL

/* Constants for setting the RNC register */
#define XHI_CONFIGURE              0x0UL
#define XHI_READBACK               0x1UL

/* Constants for the Done register */
#define XHI_NOT_FINISHED           0x0UL
#define XHI_FINISHED               0x1UL

#define XHI_BUFFER_START 0

/**
 * buffer_icap_get_status - Get the contents of the status register.
 * @drvdata: a pointer to the drvdata.
 *
 * The status register contains the ICAP status and the done bit.
 *
 * D8 - cfgerr
 * D7 - dalign
 * D6 - rip
 * D5 - in_abort_l
 * D4 - Always 1
 * D3 - Always 1
 * D2 - Always 1
 * D1 - Always 1
 * D0 - Done bit
 **/
u32 buffer_icap_get_status(struct hwicap_drvdata *drvdata)
{
	return in_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET);
}

/**
 * buffer_icap_get_bram - Reads data from the storage buffer bram.
 * @base_address: contains the base address of the component.
 * @offset: The word offset from which the data should be read.
 *
 * A bram is used as a configuration memory cache.  One frame of data can
 * be stored in this "storage buffer".
 **/
static inline u32 buffer_icap_get_bram(void __iomem *base_address,
		u32 offset)
{
	return in_be32(base_address + (offset << 2));
}

/**
 * buffer_icap_busy - Return true if the icap device is busy
 * @base_address: is the base address of the device
 *
 * The queries the low order bit of the status register, which
 * indicates whether the current configuration or readback operation
 * has completed.
 **/
static inline bool buffer_icap_busy(void __iomem *base_address)
{
	u32 status = in_be32(base_address + XHI_STATUS_REG_OFFSET);
	return (status & 1) == XHI_NOT_FINISHED;
}

/**
 * buffer_icap_set_size - Set the size register.
 * @base_address: is the base address of the device
 * @data: The size in bytes.
 *
 * The size register holds the number of 8 bit bytes to transfer between
 * bram and the icap (or icap to bram).
 **/
static inline void buffer_icap_set_size(void __iomem *base_address,
		u32 data)
{
	out_be32(base_address + XHI_SIZE_REG_OFFSET, data);
}

/**
 * buffer_icap_set_offset - Set the bram offset register.
 * @base_address: contains the base address of the device.
 * @data: is the value to be written to the data register.
 *
 * The bram offset register holds the starting bram address to transfer
 * data from during configuration or write data to during readback.
 **/
static inline void buffer_icap_set_offset(void __iomem *base_address,
		u32 data)
{
	out_be32(base_address + XHI_BRAM_OFFSET_REG_OFFSET, data);
}

/**
 * buffer_icap_set_rnc - Set the RNC (Readback not Configure) register.
 * @base_address: contains the base address of the device.
 * @data: is the value to be written to the data register.
 *
 * The RNC register determines the direction of the data transfer.  It
 * controls whether a configuration or readback take place.  Writing to
 * this register initiates the transfer.  A value of 1 initiates a
 * readback while writing a value of 0 initiates a configuration.
 **/
static inline void buffer_icap_set_rnc(void __iomem *base_address,
		u32 data)
{
	out_be32(base_address + XHI_RNC_REG_OFFSET, data);
}

/**
 * buffer_icap_set_bram - Write data to the storage buffer bram.
 * @base_address: contains the base address of the component.
 * @offset: The word offset at which the data should be written.
 * @data: The value to be written to the bram offset.
 *
 * A bram is used as a configuration memory cache.  One frame of data can
 * be stored in this "storage buffer".
 **/
static inline void buffer_icap_set_bram(void __iomem *base_address,
		u32 offset, u32 data)
{
	out_be32(base_address + (offset << 2), data);
}

/**
 * buffer_icap_device_read - Transfer bytes from ICAP to the storage buffer.
 * @drvdata: a pointer to the drvdata.
 * @offset: The storage buffer start address.
 * @count: The number of words (32 bit) to read from the
 *           device (ICAP).
 **/
static int buffer_icap_device_read(struct hwicap_drvdata *drvdata,
		u32 offset, u32 count)
{

	s32 retries = 0;
	void __iomem *base_address = drvdata->base_address;

	if (buffer_icap_busy(base_address))
		return -EBUSY;

	if ((offset + count) > XHI_MAX_BUFFER_INTS)
		return -EINVAL;

	/* setSize count*4 to get bytes. */
	buffer_icap_set_size(base_address, (count << 2));
	buffer_icap_set_offset(base_address, offset);
	buffer_icap_set_rnc(base_address, XHI_READBACK);

	while (buffer_icap_busy(base_address)) {
		retries++;
		if (retries > XHI_MAX_RETRIES)
			return -EBUSY;
	}
	return 0;

};

/**
 * buffer_icap_device_write - Transfer bytes from ICAP to the storage buffer.
 * @drvdata: a pointer to the drvdata.
 * @offset: The storage buffer start address.
 * @count: The number of words (32 bit) to read from the
 *           device (ICAP).
 **/
static int buffer_icap_device_write(struct hwicap_drvdata *drvdata,
		u32 offset, u32 count)
{

	s32 retries = 0;
	void __iomem *base_address = drvdata->base_address;

	if (buffer_icap_busy(base_address))
		return -EBUSY;

	if ((offset + count) > XHI_MAX_BUFFER_INTS)
		return -EINVAL;

	/* setSize count*4 to get bytes. */
	buffer_icap_set_size(base_address, count << 2);
	buffer_icap_set_offset(base_address, offset);
	buffer_icap_set_rnc(base_address, XHI_CONFIGURE);

	while (buffer_icap_busy(base_address)) {
		retries++;
		if (retries > XHI_MAX_RETRIES)
			return -EBUSY;
	}
	return 0;

};

/**
 * buffer_icap_reset - Reset the logic of the icap device.
 * @drvdata: a pointer to the drvdata.
 *
 * Writing to the status register resets the ICAP logic in an internal
 * version of the core.  For the version of the core published in EDK,
 * this is a noop.
 **/
void buffer_icap_reset(struct hwicap_drvdata *drvdata)
{
    out_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET, 0xFEFE);
}

/**
 * buffer_icap_set_configuration - Load a partial bitstream from system memory.
 * @drvdata: a pointer to the drvdata.
 * @data: Kernel address of the partial bitstream.
 * @size: the size of the partial bitstream in 32 bit words.
 **/
int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
			     u32 size)
{
	int status;
	s32 buffer_count = 0;
	s32 num_writes = 0;
	bool dirty = false;
	u32 i;
	void __iomem *base_address = drvdata->base_address;

	/* Loop through all the data */
	for (i = 0, buffer_count = 0; i < size; i++) {

		/* Copy data to bram */
		buffer_icap_set_bram(base_address, buffer_count, data[i]);
		dirty = true;

		if (buffer_count < XHI_MAX_BUFFER_INTS - 1) {
			buffer_count++;
			continue;
		}

		/* Write data to ICAP */
		status = buffer_icap_device_write(
				drvdata,
				XHI_BUFFER_START,
				XHI_MAX_BUFFER_INTS);
		if (status != 0) {
			/* abort. */
			buffer_icap_reset(drvdata);
			return status;
		}

		buffer_count = 0;
		num_writes++;
		dirty = false;
	}

	/* Write unwritten data to ICAP */
	if (dirty) {
		/* Write data to ICAP */
		status = buffer_icap_device_write(drvdata, XHI_BUFFER_START,
					     buffer_count);
		if (status != 0) {
			/* abort. */
			buffer_icap_reset(drvdata);
		}
		return status;
	}

	return 0;
};

/**
 * buffer_icap_get_configuration - Read configuration data from the device.
 * @drvdata: a pointer to the drvdata.
 * @data: Address of the data representing the partial bitstream
 * @size: the size of the partial bitstream in 32 bit words.
 **/
int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data,
			     u32 size)
{
	int status;
	s32 buffer_count = 0;
	s32 read_count = 0;
	u32 i;
	void __iomem *base_address = drvdata->base_address;

	/* Loop through all the data */
	for (i = 0, buffer_count = XHI_MAX_BUFFER_INTS; i < size; i++) {
		if (buffer_count == XHI_MAX_BUFFER_INTS) {
			u32 words_remaining = size - i;
			u32 words_to_read =
				words_remaining <
				XHI_MAX_BUFFER_INTS ? words_remaining :
				XHI_MAX_BUFFER_INTS;

			/* Read data from ICAP */
			status = buffer_icap_device_read(
					drvdata,
					XHI_BUFFER_START,
					words_to_read);
			if (status != 0) {
				/* abort. */
				buffer_icap_reset(drvdata);
				return status;
			}

			buffer_count = 0;
			read_count++;
		}

		/* Copy data from bram */
		data[i] = buffer_icap_get_bram(base_address, buffer_count);
		buffer_count++;
	}

	return 0;
};