1/*-
2 * Copyright (c) 2017 Chelsio Communications, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/types.h>
31#include <sys/param.h>
32
33#include "common/common.h"
34#include "common/t4_regs.h"
35#include "cudbg.h"
36#include "cudbg_lib_common.h"
37
38enum {
39	SF_ATTEMPTS = 10,		/* max retries for SF operations */
40
41	/* flash command opcodes */
42	SF_PROG_PAGE	= 2,	/* program page */
43	SF_WR_DISABLE	= 4,	/* disable writes */
44	SF_RD_STATUS	= 5,	/* read status register */
45	SF_WR_ENABLE	= 6,	/* enable writes */
46	SF_RD_DATA_FAST = 0xb,	/* read flash */
47	SF_RD_ID	= 0x9f, /* read ID */
48	SF_ERASE_SECTOR = 0xd8, /* erase sector */
49};
50
51int write_flash(struct adapter *adap, u32 start_sec, void *data, u32 size);
52int read_flash(struct adapter *adap, u32 start_sec , void *data, u32 size,
53		u32 start_address);
54
55void
56update_skip_size(struct cudbg_flash_sec_info *sec_info, u32 size)
57{
58	sec_info->skip_size += size;
59}
60
61static
62void set_sector_availability(struct cudbg_flash_sec_info *sec_info,
63    int sector_nu, int avail)
64{
65	sector_nu -= CUDBG_START_SEC;
66	if (avail)
67		set_dbg_bitmap(sec_info->sec_bitmap, sector_nu);
68	else
69		reset_dbg_bitmap(sec_info->sec_bitmap, sector_nu);
70}
71
72/* This function will return empty sector available for filling */
73static int
74find_empty_sec(struct cudbg_flash_sec_info *sec_info)
75{
76	int i, index, bit;
77
78	for (i = CUDBG_START_SEC; i < CUDBG_SF_MAX_SECTOR; i++) {
79		index = (i - CUDBG_START_SEC) / 8;
80		bit = (i - CUDBG_START_SEC) % 8;
81		if (!(sec_info->sec_bitmap[index] & (1 << bit)))
82			return i;
83	}
84
85	return CUDBG_STATUS_FLASH_FULL;
86}
87
88/* This function will get header initially. If header is already there
89 * then it will update that header */
90static void update_headers(void *handle, struct cudbg_buffer *dbg_buff,
91		    u64 timestamp, u32 cur_entity_hdr_offset,
92		    u32 start_offset, u32 ext_size)
93{
94	struct cudbg_private *priv = handle;
95	struct cudbg_flash_sec_info *sec_info = &priv->sec_info;
96	void *sec_hdr;
97	struct cudbg_hdr *cudbg_hdr;
98	struct cudbg_flash_hdr *flash_hdr;
99	struct cudbg_entity_hdr *entity_hdr;
100	u32 hdr_offset;
101	u32 data_hdr_size;
102	u32 total_hdr_size;
103	u32 sec_hdr_start_addr;
104
105	data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) +
106				sizeof(struct cudbg_hdr);
107	total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr);
108	sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size;
109	sec_hdr  = sec_info->sec_data + sec_hdr_start_addr;
110
111	flash_hdr = (struct cudbg_flash_hdr *)(sec_hdr);
112	cudbg_hdr = (struct cudbg_hdr *)dbg_buff->data;
113
114	/* initially initialize flash hdr and copy all data headers and
115	 * in next calling (else part) copy only current entity header
116	 */
117	if ((start_offset - sec_info->skip_size) == data_hdr_size) {
118		flash_hdr->signature = CUDBG_FL_SIGNATURE;
119		flash_hdr->major_ver = CUDBG_FL_MAJOR_VERSION;
120		flash_hdr->minor_ver = CUDBG_FL_MINOR_VERSION;
121		flash_hdr->build_ver = CUDBG_FL_BUILD_VERSION;
122		flash_hdr->hdr_len = sizeof(struct cudbg_flash_hdr);
123		hdr_offset =  sizeof(struct cudbg_flash_hdr);
124
125		memcpy((void *)((char *)sec_hdr + hdr_offset),
126		       (void *)((char *)dbg_buff->data), data_hdr_size);
127	} else
128		memcpy((void *)((char *)sec_hdr +
129			sizeof(struct cudbg_flash_hdr) +
130			cur_entity_hdr_offset),
131			(void *)((char *)dbg_buff->data +
132			cur_entity_hdr_offset),
133			sizeof(struct cudbg_entity_hdr));
134
135	hdr_offset = data_hdr_size + sizeof(struct cudbg_flash_hdr);
136	flash_hdr->data_len = cudbg_hdr->data_len - sec_info->skip_size;
137	flash_hdr->timestamp = timestamp;
138
139	entity_hdr = (struct cudbg_entity_hdr *)((char *)sec_hdr +
140		      sizeof(struct cudbg_flash_hdr) +
141		      cur_entity_hdr_offset);
142	/* big entity like mc need to be skipped */
143	entity_hdr->start_offset -= sec_info->skip_size;
144
145	cudbg_hdr = (struct cudbg_hdr *)((char *)sec_hdr +
146			sizeof(struct cudbg_flash_hdr));
147	cudbg_hdr->data_len = flash_hdr->data_len;
148	flash_hdr->data_len += ext_size;
149}
150
151/* Write CUDBG data into serial flash */
152int cudbg_write_flash(void *handle, u64 timestamp, void *data,
153		      u32 start_offset, u32 cur_entity_hdr_offset,
154		      u32 cur_entity_size,
155		      u32 ext_size)
156{
157	struct cudbg_private *priv = handle;
158	struct cudbg_init *cudbg_init = &priv->dbg_init;
159	struct cudbg_flash_sec_info *sec_info = &priv->sec_info;
160	struct adapter *adap = cudbg_init->adap;
161	struct cudbg_flash_hdr *flash_hdr = NULL;
162	struct cudbg_buffer *dbg_buff = (struct cudbg_buffer *)data;
163	u32 data_hdr_size;
164	u32 total_hdr_size;
165	u32 tmp_size;
166	u32 sec_data_offset;
167	u32 sec_hdr_start_addr;
168	u32 sec_data_size;
169	u32 space_left;
170	int rc = 0;
171	int sec;
172
173	data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) +
174			sizeof(struct cudbg_hdr);
175	total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr);
176	sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size;
177	sec_data_size = sec_hdr_start_addr;
178
179	cudbg_init->print("\tWriting %u bytes to flash\n", cur_entity_size);
180
181	/* this function will get header if sec_info->sec_data does not
182	 * have any header and
183	 * will update the header if it has header
184	 */
185	update_headers(handle, dbg_buff, timestamp,
186		       cur_entity_hdr_offset,
187		       start_offset, ext_size);
188
189	if (ext_size) {
190		cur_entity_size += sizeof(struct cudbg_entity_hdr);
191		start_offset = dbg_buff->offset - cur_entity_size;
192	}
193
194	flash_hdr = (struct cudbg_flash_hdr *)(sec_info->sec_data +
195			sec_hdr_start_addr);
196
197	if (flash_hdr->data_len > CUDBG_FLASH_SIZE) {
198		rc = CUDBG_STATUS_FLASH_FULL;
199		goto out;
200	}
201
202	space_left = CUDBG_FLASH_SIZE - flash_hdr->data_len;
203
204	if (cur_entity_size > space_left) {
205		rc = CUDBG_STATUS_FLASH_FULL;
206		goto out;
207	}
208
209	while (cur_entity_size > 0) {
210		sec = find_empty_sec(sec_info);
211		if (sec_info->par_sec) {
212			sec_data_offset = sec_info->par_sec_offset;
213			set_sector_availability(sec_info, sec_info->par_sec, 0);
214			sec_info->par_sec = 0;
215			sec_info->par_sec_offset = 0;
216
217		} else {
218			sec_info->cur_seq_no++;
219			flash_hdr->sec_seq_no = sec_info->cur_seq_no;
220			sec_data_offset = 0;
221		}
222
223		if (cur_entity_size + sec_data_offset > sec_data_size) {
224			tmp_size = sec_data_size - sec_data_offset;
225		} else {
226			tmp_size = cur_entity_size;
227			sec_info->par_sec = sec;
228			sec_info->par_sec_offset = cur_entity_size +
229						  sec_data_offset;
230		}
231
232		memcpy((void *)((char *)sec_info->sec_data + sec_data_offset),
233		       (void *)((char *)dbg_buff->data + start_offset),
234		       tmp_size);
235
236		rc = write_flash(adap, sec, sec_info->sec_data,
237				CUDBG_SF_SECTOR_SIZE);
238		if (rc)
239			goto out;
240
241		cur_entity_size -= tmp_size;
242		set_sector_availability(sec_info, sec, 1);
243		start_offset += tmp_size;
244	}
245out:
246	return rc;
247}
248
249int write_flash(struct adapter *adap, u32 start_sec, void *data, u32 size)
250{
251	unsigned int addr;
252	unsigned int i, n;
253	unsigned int sf_sec_size;
254	int rc = 0;
255
256	u8 *ptr = (u8 *)data;
257
258	sf_sec_size = adap->params.sf_size/adap->params.sf_nsec;
259
260	addr =  start_sec * CUDBG_SF_SECTOR_SIZE;
261	i = DIV_ROUND_UP(size,/* # of sectors spanned */
262			sf_sec_size);
263
264	rc = t4_flash_erase_sectors(adap, start_sec,
265		   start_sec + i - 1);
266	/*
267	 * If size == 0 then we're simply erasing the FLASH sectors associated
268	 * with the on-adapter OptionROM Configuration File.
269	 */
270
271	if (rc || size == 0)
272		goto out;
273
274	/* this will write to the flash up to SF_PAGE_SIZE at a time */
275	for (i = 0; i < size; i += SF_PAGE_SIZE) {
276		if ((size - i) <  SF_PAGE_SIZE)
277			n = size - i;
278		else
279			n = SF_PAGE_SIZE;
280		rc = t4_write_flash(adap, addr, n, ptr, 0);
281		if (rc)
282			goto out;
283
284		addr += n;
285		ptr += n;
286	}
287
288	return 0;
289out:
290	return rc;
291}
292
293int cudbg_read_flash_details(void *handle, struct cudbg_flash_hdr *data)
294{
295	int rc;
296	rc = cudbg_read_flash(handle, (void *)data,
297			      sizeof(struct cudbg_flash_hdr), 0);
298
299	return rc;
300}
301
302int cudbg_read_flash_data(void *handle, void *buf, u32 buf_size)
303{
304	int rc;
305	u32 total_hdr_size, data_header_size;
306	void *payload = NULL;
307	u32 payload_size = 0;
308
309	data_header_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) +
310		sizeof(struct cudbg_hdr);
311	total_hdr_size = data_header_size + sizeof(struct cudbg_flash_hdr);
312
313	/* Copy flash header to buffer */
314	rc = cudbg_read_flash(handle, buf, total_hdr_size, 0);
315	if (rc != 0)
316		goto out;
317	payload = (char *)buf + total_hdr_size;
318	payload_size  = buf_size - total_hdr_size;
319
320	/* Reading flash data to buf */
321	rc = cudbg_read_flash(handle, payload, payload_size, 1);
322	if (rc != 0)
323		goto out;
324
325out:
326	return rc;
327}
328
329int cudbg_read_flash(void *handle, void *data, u32 size, int data_flag)
330{
331	struct cudbg_private *priv = handle;
332	struct cudbg_init *cudbg_init = &priv->dbg_init;
333	struct cudbg_flash_sec_info *sec_info = &priv->sec_info;
334	struct adapter *adap = cudbg_init->adap;
335	struct cudbg_flash_hdr flash_hdr;
336	u32 total_hdr_size;
337	u32 data_hdr_size;
338	u32 sec_hdr_start_addr;
339	u32 tmp_size;
340	u32 data_offset = 0;
341	u32 i, j;
342	int rc;
343
344	rc = t4_get_flash_params(adap);
345	if (rc) {
346		cudbg_init->print("\nGet flash params failed."
347			"Try Again...readflash\n\n");
348		return rc;
349	}
350
351	data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) +
352			sizeof(struct cudbg_hdr);
353	total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr);
354	sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size;
355
356	if (!data_flag) {
357		/* fill header */
358		if (!sec_info->max_timestamp) {
359			/* finding max time stamp because it may
360			 * have older filled sector also
361			 */
362			memset(&flash_hdr, 0, sizeof(struct cudbg_flash_hdr));
363			rc = read_flash(adap, CUDBG_START_SEC, &flash_hdr,
364				sizeof(struct cudbg_flash_hdr),
365				sec_hdr_start_addr);
366
367			if (flash_hdr.signature == CUDBG_FL_SIGNATURE) {
368				sec_info->max_timestamp = flash_hdr.timestamp;
369			} else {
370				rc = read_flash(adap, CUDBG_START_SEC + 1,
371					&flash_hdr,
372					sizeof(struct cudbg_flash_hdr),
373					sec_hdr_start_addr);
374
375				if (flash_hdr.signature == CUDBG_FL_SIGNATURE)
376					sec_info->max_timestamp =
377							flash_hdr.timestamp;
378				else {
379					cudbg_init->print("\n\tNo cudbg dump "\
380							  "found in flash\n\n");
381					return CUDBG_STATUS_NO_SIGNATURE;
382				}
383
384			}
385
386			/* finding max sequence number because max sequenced
387			 * sector has updated header
388			 */
389			for (i = CUDBG_START_SEC; i <
390					CUDBG_SF_MAX_SECTOR; i++) {
391				memset(&flash_hdr, 0,
392				       sizeof(struct cudbg_flash_hdr));
393				rc = read_flash(adap, i, &flash_hdr,
394						sizeof(struct cudbg_flash_hdr),
395						sec_hdr_start_addr);
396
397				if (flash_hdr.signature == CUDBG_FL_SIGNATURE &&
398				    sec_info->max_timestamp ==
399				    flash_hdr.timestamp &&
400				    sec_info->max_seq_no <=
401				    flash_hdr.sec_seq_no) {
402					if (sec_info->max_seq_no ==
403					    flash_hdr.sec_seq_no) {
404						if (sec_info->hdr_data_len <
405						    flash_hdr.data_len)
406							sec_info->max_seq_sec = i;
407					} else {
408						sec_info->max_seq_sec = i;
409						sec_info->hdr_data_len =
410							flash_hdr.data_len;
411					}
412					sec_info->max_seq_no = flash_hdr.sec_seq_no;
413				}
414			}
415		}
416		rc = read_flash(adap, sec_info->max_seq_sec,
417				(struct cudbg_flash_hdr *)data,
418				size, sec_hdr_start_addr);
419
420		if (rc)
421			cudbg_init->print("Read flash header failed, rc %d\n",
422					rc);
423
424		return rc;
425	}
426
427	/* finding sector sequence sorted */
428	for (i = 1; i <= sec_info->max_seq_no; i++) {
429		for (j = CUDBG_START_SEC; j < CUDBG_SF_MAX_SECTOR; j++) {
430			memset(&flash_hdr, 0, sizeof(struct cudbg_flash_hdr));
431			rc = read_flash(adap, j, &flash_hdr,
432				sizeof(struct cudbg_flash_hdr),
433				sec_hdr_start_addr);
434
435			if (flash_hdr.signature ==
436					CUDBG_FL_SIGNATURE &&
437					sec_info->max_timestamp ==
438					flash_hdr.timestamp &&
439					flash_hdr.sec_seq_no == i) {
440				if (size + total_hdr_size >
441						CUDBG_SF_SECTOR_SIZE)
442					tmp_size = CUDBG_SF_SECTOR_SIZE -
443						total_hdr_size;
444				else
445					tmp_size =  size;
446
447				if ((i != sec_info->max_seq_no) ||
448				    (i == sec_info->max_seq_no &&
449				    j == sec_info->max_seq_sec)){
450					/* filling data buffer with sector data
451					 * except sector header
452					 */
453					rc = read_flash(adap, j,
454							(void *)((char *)data +
455							data_offset),
456							tmp_size, 0);
457					data_offset += (tmp_size);
458					size -= (tmp_size);
459					break;
460				}
461			}
462		}
463	}
464
465	return rc;
466}
467
468int read_flash(struct adapter *adap, u32 start_sec , void *data, u32 size,
469		u32 start_address)
470{
471	unsigned int addr, i, n;
472	int rc;
473	u32 *ptr = (u32 *)data;
474	addr = start_sec * CUDBG_SF_SECTOR_SIZE + start_address;
475	size = size / 4;
476	for (i = 0; i < size; i += SF_PAGE_SIZE) {
477		if ((size - i) <  SF_PAGE_SIZE)
478			n = size - i;
479		else
480			n = SF_PAGE_SIZE;
481		rc = t4_read_flash(adap, addr, n, ptr, 0);
482		if (rc)
483			goto out;
484
485		addr = addr + (n*4);
486		ptr += n;
487	}
488
489	return 0;
490out:
491	return rc;
492}
493