adwlib.c revision 40024
1/*
2 * Low level routines for Second Generation
3 * Advanced Systems Inc. SCSI controllers chips
4 *
5 * Copyright (c) 1998 Justin Gibbs.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions, and the following disclaimer,
13 *    without modification, immediately at the beginning of the file.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote products
18 *    derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
24 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *      $Id$
33 */
34/*
35 * Ported from:
36 * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
37 *
38 * Copyright (c) 1995-1998 Advanced System Products, Inc.
39 * All Rights Reserved.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that redistributions of source
43 * code retain the above copyright notice and this comment without
44 * modification.
45 */
46#include <sys/queue.h>
47#include <sys/systm.h>
48
49#include <machine/bus_pio.h>
50#include <machine/bus_memio.h>
51#include <machine/bus.h>
52#include <machine/clock.h>
53
54#include <cam/cam.h>
55#include <cam/scsi/scsi_all.h>
56
57#include <dev/advansys/adwlib.h>
58
59struct adw_eeprom adw_default_eeprom = {
60	ADW_EEPROM_BIOS_ENABLE,	/* cfg_lsw */
61	0x0000,			/* cfg_msw */
62	0xFFFF,			/* disc_enable */
63	0xFFFF,			/* wdtr_able */
64	0xFFFF,			/* sdtr_able */
65	0xFFFF,			/* start_motor */
66	0xFFFF,			/* tagqng_able */
67	0xFFFF,			/* bios_scan */
68	0,			/* scam_tolerant */
69	7,			/* adapter_scsi_id */
70	0,			/* bios_boot_delay */
71	3,			/* scsi_reset_delay */
72	0,			/* bios_id_lun */
73	0,			/* termination */
74	0,			/* reserved1 */
75	{			/* Bios Ctrl */
76	  1, 1, 1, 1, 1,
77	  1, 1, 1, 1, 1,
78	},
79	0xFFFF,			/* ultra_able */
80	0,			/* reserved2 */
81	ADW_DEF_MAX_HOST_QNG,	/* max_host_qng */
82	ADW_DEF_MAX_DVC_QNG,	/* max_dvc_qng */
83	0,			/* dvc_cntl */
84	0,			/* bug_fix */
85	{ 0, 0, 0 },		/* serial_number */
86	0,			/* check_sum */
87	{			/* oem_name[16] */
88	  0, 0, 0, 0, 0, 0, 0, 0,
89	  0, 0, 0, 0, 0, 0, 0, 0
90	},
91	0,			/* dvc_err_code */
92	0,			/* adv_err_code */
93	0,			/* adv_err_addr */
94	0,			/* saved_dvc_err_code */
95	0,			/* saved_adv_err_code */
96	0,			/* saved_adv_err_addr */
97	0			/* num_of_err */
98};
99
100static u_int16_t	adw_eeprom_read_16(struct adw_softc *adw, int addr);
101static void		adw_eeprom_write_16(struct adw_softc *adw, int addr,
102					    u_int data);
103static void		adw_eeprom_wait(struct adw_softc *adw);
104
105int
106adw_find_signature(bus_space_tag_t tag, bus_space_handle_t bsh)
107{
108	if (bus_space_read_1(tag, bsh, ADW_SIGNATURE_BYTE) == ADW_CHIP_ID_BYTE
109	 && bus_space_read_2(tag, bsh, ADW_SIGNATURE_WORD) == ADW_CHIP_ID_WORD)
110		return (1);
111	return (0);
112}
113
114/*
115 * Reset Chip.
116 */
117void
118adw_reset_chip(struct adw_softc *adw)
119{
120	adw_outw(adw, ADW_CTRL_REG, ADW_CTRL_REG_CMD_RESET);
121	DELAY(100);
122	adw_outw(adw, ADW_CTRL_REG, ADW_CTRL_REG_CMD_WR_IO_REG);
123
124	/*
125	 * Initialize Chip registers.
126	 */
127	adw_outb(adw, ADW_MEM_CFG,
128		 adw_inb(adw, ADW_MEM_CFG) | ADW_MEM_CFG_RAM_SZ_8KB);
129
130	adw_outw(adw, ADW_SCSI_CFG1,
131		 adw_inw(adw, ADW_SCSI_CFG1) & ~ADW_SCSI_CFG1_BIG_ENDIAN);
132
133	/*
134	 * Setting the START_CTL_EM_FU 3:2 bits sets a FIFO threshold
135	 * of 128 bytes. This register is only accessible to the host.
136	 */
137	adw_outb(adw, ADW_DMA_CFG0,
138		 ADW_DMA_CFG0_START_CTL_EM_FU|ADW_DMA_CFG0_READ_CMD_MRM);
139}
140
141/*
142 * Read the specified EEPROM location
143 */
144static u_int16_t
145adw_eeprom_read_16(struct adw_softc *adw, int addr)
146{
147	adw_outw(adw, ADW_EEP_CMD, ADW_EEP_CMD_READ | addr);
148	adw_eeprom_wait(adw);
149	return (adw_inw(adw, ADW_EEP_DATA));
150}
151
152static void
153adw_eeprom_write_16(struct adw_softc *adw, int addr, u_int data)
154{
155	adw_outw(adw, ADW_EEP_DATA, data);
156	adw_outw(adw, ADW_EEP_CMD, ADW_EEP_CMD_WRITE | addr);
157	adw_eeprom_wait(adw);
158}
159
160/*
161 * Wait for and EEPROM command to complete
162 */
163static void
164adw_eeprom_wait(struct adw_softc *adw)
165{
166	int i;
167
168	for (i = 0; i < ADW_EEP_DELAY_MS; i++) {
169		if ((adw_inw(adw, ADW_EEP_CMD) & ADW_EEP_CMD_DONE) != 0)
170            		break;
171		DELAY(1000);
172    	}
173	if (i == ADW_EEP_DELAY_MS)
174		panic("%s: Timedout Reading EEPROM", adw_name(adw));
175}
176
177/*
178 * Read EEPROM configuration into the specified buffer.
179 *
180 * Return a checksum based on the EEPROM configuration read.
181 */
182u_int16_t
183adw_eeprom_read(struct adw_softc *adw, struct adw_eeprom *eep_buf)
184{
185	u_int16_t *wbuf;
186	u_int16_t  wval;
187	u_int16_t  chksum;
188	int	   eep_addr;
189
190	wbuf = (u_int16_t *)eep_buf;
191	chksum = 0;
192
193	for (eep_addr = ADW_EEP_DVC_CFG_BEGIN;
194	     eep_addr < ADW_EEP_DVC_CFG_END;
195	     eep_addr++, wbuf++) {
196		wval = adw_eeprom_read_16(adw, eep_addr);
197		chksum += wval;
198		*wbuf = wval;
199	}
200
201	/* checksum field is not counted in the checksum */
202	*wbuf = adw_eeprom_read_16(adw, eep_addr);
203	wbuf++;
204
205	/* Driver seeprom variables are not included in the checksum */
206	for (eep_addr = ADW_EEP_DVC_CTL_BEGIN;
207	     eep_addr < ADW_EEP_MAX_WORD_ADDR;
208	     eep_addr++, wbuf++)
209		*wbuf = adw_eeprom_read_16(adw, eep_addr);
210
211	return (chksum);
212}
213
214void
215adw_eeprom_write(struct adw_softc *adw, struct adw_eeprom *eep_buf)
216{
217	u_int16_t *wbuf;
218	u_int16_t  addr;
219	u_int16_t  chksum;
220
221	wbuf = (u_int16_t *)eep_buf;
222	chksum = 0;
223
224	adw_outw(adw, ADW_EEP_CMD, ADW_EEP_CMD_WRITE_ABLE);
225	adw_eeprom_wait(adw);
226
227	/*
228	 * Write EEPROM until checksum.
229	 */
230	for (addr = ADW_EEP_DVC_CFG_BEGIN;
231	     addr < ADW_EEP_DVC_CFG_END; addr++, wbuf++) {
232		chksum += *wbuf;
233		adw_eeprom_write_16(adw, addr, *wbuf);
234	}
235
236	/*
237	 * Write calculated EEPROM checksum
238	 */
239	adw_eeprom_write_16(adw, addr, chksum);
240
241	/* skip over buffer's checksum */
242	wbuf++;
243
244	/*
245	 * Write the rest.
246	 */
247	for (addr = ADW_EEP_DVC_CTL_BEGIN;
248	     addr < ADW_EEP_MAX_WORD_ADDR; addr++, wbuf++)
249		adw_eeprom_write_16(adw, addr, *wbuf);
250
251	adw_outw(adw, ADW_EEP_CMD, ADW_EEP_CMD_WRITE_DISABLE);
252	adw_eeprom_wait(adw);
253}
254
255int
256adw_init_chip(struct adw_softc *adw, u_int term_scsicfg1)
257{
258	u_int8_t   biosmem[ADW_MC_BIOSLEN];
259	u_int16_t *mcodebuf;
260	u_int	   addr;
261	u_int	   end_addr;
262	u_int	   checksum;
263	u_int	   scsicfg1;
264	u_int	   i;
265
266	/*
267	 * Save the RISC memory BIOS region before writing the microcode.
268	 * The BIOS may already be loaded and using its RISC LRAM region
269	 * so its region must be saved and restored.
270	 */
271	for (addr = 0; addr < ADW_MC_BIOSLEN; addr++)
272		biosmem[addr] = adw_lram_read_8(adw, ADW_MC_BIOSMEM + addr);
273
274	/*
275	 * Load the Microcode.  Casting here was less work than
276	 * reformatting the supplied microcode into an array of
277	 * 16bit values...
278	 */
279	mcodebuf = (u_int16_t *)adw_mcode;
280	adw_outw(adw, ADW_RAM_ADDR, 0);
281	for (addr = 0; addr < adw_mcode_size/2; addr++)
282		adw_outw(adw, ADW_RAM_DATA, mcodebuf[addr]);
283
284	/*
285	 * Clear the rest of LRAM.
286	 */
287	for (; addr < ADW_CONDOR_MEMSIZE/2; addr++)
288		adw_outw(adw, ADW_RAM_DATA, 0);
289
290	/*
291	 * Verify the microcode checksum.
292	 */
293	checksum = 0;
294	adw_outw(adw, ADW_RAM_ADDR, 0);
295	for (addr = 0; addr < adw_mcode_size/2; addr++)
296		checksum += adw_inw(adw, ADW_RAM_DATA);
297
298	if (checksum != adw_mcode_chksum) {
299		printf("%s: Firmware load failed!\n", adw_name(adw));
300		return (-1);
301	}
302
303	/*
304	 * Restore the RISC memory BIOS region.
305	 */
306	for (addr = 0; addr < ADW_MC_BIOSLEN; addr++)
307		adw_lram_write_8(adw, addr + ADW_MC_BIOSLEN, biosmem[addr]);
308
309	/*
310	 * Calculate and write the microcode code checksum to
311	 * the microcode code checksum location.
312	 */
313	addr = adw_lram_read_16(adw, ADW_MC_CODE_BEGIN_ADDR) / 2;
314	end_addr = adw_lram_read_16(adw, ADW_MC_CODE_END_ADDR) / 2;
315	checksum = 0;
316	for (; addr < end_addr; addr++)
317		checksum += mcodebuf[addr];
318	adw_lram_write_16(adw, ADW_MC_CODE_CHK_SUM, checksum);
319
320	/*
321	 * Initialize microcode operating variables
322	 */
323	adw_lram_write_16(adw, ADW_MC_ADAPTER_SCSI_ID, adw->initiator_id);
324
325	/*
326	 * Leave WDTR and SDTR negotiation disabled until the XPT has
327	 * informed us of device capabilities, but do set the ultra mask
328	 * in case we receive an SDTR request from the target before we
329	 * negotiate.  We turn on tagged queuing at the microcode level
330	 * for all devices, and modulate this on a per command basis.
331	 */
332	adw_lram_write_16(adw, ADW_MC_ULTRA_ABLE, adw->user_ultra);
333	adw_lram_write_16(adw, ADW_MC_DISC_ENABLE, adw->user_discenb);
334	adw_lram_write_16(adw, ADW_MC_TAGQNG_ABLE, ~0);
335
336	/*
337	 * Set SCSI_CFG0 Microcode Default Value.
338	 *
339	 * The microcode will set the SCSI_CFG0 register using this value
340	 * after it is started.
341	 */
342	adw_lram_write_16(adw, ADW_MC_DEFAULT_SCSI_CFG0,
343			  ADW_SCSI_CFG0_PARITY_EN|ADW_SCSI_CFG0_SEL_TMO_LONG|
344			  ADW_SCSI_CFG0_OUR_ID_EN|adw->initiator_id);
345
346	/*
347	 * Determine SCSI_CFG1 Microcode Default Value.
348	 *
349	 * The microcode will set the SCSI_CFG1 register using this value
350	 * after it is started below.
351	 */
352	scsicfg1 = adw_inw(adw, ADW_SCSI_CFG1);
353
354	/*
355	 * If all three connectors are in use, return an error.
356	 */
357	if ((scsicfg1 & ADW_SCSI_CFG1_ILLEGAL_CABLE_CONF_A_MASK) == 0
358	 || (scsicfg1 & ADW_SCSI_CFG1_ILLEGAL_CABLE_CONF_B_MASK) == 0) {
359		printf("%s: Illegal Cable Config!\n", adw_name(adw));
360		printf("%s: Only Two Ports may be used at a time!\n",
361		       adw_name(adw));
362		return (-1);
363	}
364
365	/*
366	 * If the internal narrow cable is reversed all of the SCSI_CTRL
367	 * register signals will be set. Check for and return an error if
368	 * this condition is found.
369	 */
370	if ((adw_inw(adw, ADW_SCSI_CTRL) & 0x3F07) == 0x3F07) {
371		printf("%s: Illegal Cable Config!\n", adw_name(adw));
372		printf("%s: Internal cable is reversed!\n", adw_name(adw));
373	        return (-1);
374	}
375
376	/*
377	 * If this is a differential board and a single-ended device
378	 * is attached to one of the connectors, return an error.
379	 */
380	if ((scsicfg1 & ADW_SCSI_CFG1_DIFF_MODE) != 0
381	 && (scsicfg1 & ADW_SCSI_CFG1_DIFF_SENSE) == 0) {
382		printf("%s: A Single Ended Device is attached to our "
383		       "differential bus!\n", adw_name(adw));
384	        return (-1);
385	}
386
387	/*
388	 * Perform automatic termination control if desired.
389	 */
390	if (term_scsicfg1 == 0) {
391        	switch(scsicfg1 & ADW_SCSI_CFG1_CABLE_DETECT) {
392		case (ADW_SCSI_CFG1_INT16_MASK|ADW_SCSI_CFG1_INT8_MASK):
393		case (ADW_SCSI_CFG1_INT16_MASK|
394		      ADW_SCSI_CFG1_INT8_MASK|ADW_SCSI_CFG1_EXT8_MASK):
395		case (ADW_SCSI_CFG1_INT16_MASK|
396		      ADW_SCSI_CFG1_INT8_MASK|ADW_SCSI_CFG1_EXT16_MASK):
397		case (ADW_SCSI_CFG1_INT16_MASK|
398		      ADW_SCSI_CFG1_EXT8_MASK|ADW_SCSI_CFG1_EXT16_MASK):
399		case (ADW_SCSI_CFG1_INT8_MASK|
400		      ADW_SCSI_CFG1_EXT8_MASK|ADW_SCSI_CFG1_EXT16_MASK):
401		case (ADW_SCSI_CFG1_INT16_MASK|ADW_SCSI_CFG1_INT8_MASK|
402		      ADW_SCSI_CFG1_EXT8_MASK|ADW_SCSI_CFG1_EXT16_MASK):
403			/* Two out of three cables missing.  Both on. */
404			term_scsicfg1 |= ADW_SCSI_CFG1_TERM_CTL_L
405				      |  ADW_SCSI_CFG1_TERM_CTL_H;
406			break;
407		case (ADW_SCSI_CFG1_INT16_MASK):
408		case (ADW_SCSI_CFG1_INT16_MASK|ADW_SCSI_CFG1_EXT8_MASK):
409		case (ADW_SCSI_CFG1_INT16_MASK|ADW_SCSI_CFG1_EXT16_MASK):
410		case (ADW_SCSI_CFG1_INT8_MASK|ADW_SCSI_CFG1_EXT16_MASK):
411		case (ADW_SCSI_CFG1_EXT8_MASK|ADW_SCSI_CFG1_EXT16_MASK):
412			/* No two 16bit cables present.  High on. */
413			term_scsicfg1 |= ADW_SCSI_CFG1_TERM_CTL_H;
414			break;
415		case (ADW_SCSI_CFG1_INT8_MASK):
416		case (ADW_SCSI_CFG1_INT8_MASK|ADW_SCSI_CFG1_EXT8_MASK):
417			/* Wide -> Wide or Narrow -> Wide. Both off */
418			break;
419		}
420        }
421
422	/* Tell the user about our decission */
423	switch (term_scsicfg1 & ADW_SCSI_CFG1_TERM_CTL_MASK) {
424	case ADW_SCSI_CFG1_TERM_CTL_MASK:
425		printf("High & Low Termination Enabled, ");
426		break;
427	case ADW_SCSI_CFG1_TERM_CTL_H:
428		printf("High Termination Enabled, ");
429		break;
430	case ADW_SCSI_CFG1_TERM_CTL_L:
431		printf("Low Termination Enabled, ");
432		break;
433	default:
434		break;
435	}
436
437	/*
438	 * Invert the TERM_CTL_H and TERM_CTL_L bits and then
439	 * set 'scsicfg1'. The TERM_POL bit does not need to be
440	 * referenced, because the hardware internally inverts
441	 * the Termination High and Low bits if TERM_POL is set.
442	 */
443	term_scsicfg1 = ~term_scsicfg1 & ADW_SCSI_CFG1_TERM_CTL_MASK;
444	scsicfg1 &= ~ADW_SCSI_CFG1_TERM_CTL_MASK;
445	scsicfg1 |= term_scsicfg1 | ADW_SCSI_CFG1_TERM_CTL_MANUAL;
446
447	/*
448	 * Set SCSI_CFG1 Microcode Default Value
449	 *
450	 * Set filter value and possibly modified termination control
451	 * bits in the Microcode SCSI_CFG1 Register Value.
452	 *
453	 * The microcode will set the SCSI_CFG1 register using this value
454	 * after it is started below.
455	 */
456	adw_lram_write_16(adw, ADW_MC_DEFAULT_SCSI_CFG1,
457			  scsicfg1 | ADW_SCSI_CFG1_FLTR_11_TO_20NS);
458
459	/*
460	 * Only accept selections on our initiator target id.
461	 * This may change in target mode scenarios...
462	 */
463	adw_lram_write_16(adw, ADW_MC_DEFAULT_SEL_MASK,
464			  (0x01 << adw->initiator_id));
465
466	/*
467	 * Link all the RISC Queue Lists together in a doubly-linked
468	 * NULL terminated list.
469	 *
470	 * Skip the NULL (0) queue which is not used.
471	 */
472	for (i = 1, addr = ADW_MC_RISC_Q_LIST_BASE + ADW_MC_RISC_Q_LIST_SIZE;
473	     i < ADW_MC_RISC_Q_TOTAL_CNT;
474	     i++, addr += ADW_MC_RISC_Q_LIST_SIZE) {
475
476	        /*
477		 * Set the current RISC Queue List's
478		 * RQL_FWD and RQL_BWD pointers in a
479		 * one word write and set the state
480		 * (RQL_STATE) to free.
481		 */
482		adw_lram_write_16(adw, addr, ((i + 1) | ((i - 1) << 8)));
483		adw_lram_write_8(adw, addr + RQL_STATE, ADW_MC_QS_FREE);
484	}
485
486	/*
487	 * Set the Host and RISC Queue List pointers.
488	 *
489	 * Both sets of pointers are initialized with the same values:
490	 * ADW_MC_RISC_Q_FIRST(0x01) and ADW_MC_RISC_Q_LAST (0xFF).
491	 */
492	adw_lram_write_8(adw, ADW_MC_HOST_NEXT_READY, ADW_MC_RISC_Q_FIRST);
493	adw_lram_write_8(adw, ADW_MC_HOST_NEXT_DONE, ADW_MC_RISC_Q_LAST);
494
495	adw_lram_write_8(adw, ADW_MC_RISC_NEXT_READY, ADW_MC_RISC_Q_FIRST);
496	adw_lram_write_8(adw, ADW_MC_RISC_NEXT_DONE, ADW_MC_RISC_Q_LAST);
497
498	/*
499	 * Set up the last RISC Queue List (255) with a NULL forward pointer.
500	 */
501	adw_lram_write_16(adw, addr, (ADW_MC_NULL_Q + ((i - 1) << 8)));
502	adw_lram_write_8(adw, addr + RQL_STATE, ADW_MC_QS_FREE);
503
504	adw_outb(adw, ADW_INTR_ENABLES,
505		 ADW_INTR_ENABLE_HOST_INTR|ADW_INTR_ENABLE_GLOBAL_INTR);
506
507	adw_outw(adw, ADW_PC, adw_lram_read_16(adw, ADW_MC_CODE_BEGIN_ADDR));
508
509	return (0);
510}
511
512/*
513 * Send an idle command to the chip and optionally wait for completion.
514 */
515void
516adw_idle_cmd_send(struct adw_softc *adw, adw_idle_cmd_t cmd, u_int parameter)
517{
518	int s;
519
520	adw->idle_command_cmp = 0;
521
522	s = splcam();
523
524	if (adw->idle_cmd != ADW_IDLE_CMD_COMPLETED)
525		printf("%s: Warning! Overlapped Idle Commands Attempted\n",
526		       adw_name(adw));
527	adw->idle_cmd = cmd;
528	adw->idle_cmd_param = parameter;
529
530	/*
531	 * Write the idle command value after the idle command parameter
532	 * has been written to avoid a race condition. If the order is not
533	 * followed, the microcode may process the idle command before the
534	 * parameters have been written to LRAM.
535	 */
536	adw_lram_write_16(adw, ADW_MC_IDLE_PARA_STAT, parameter);
537    	adw_lram_write_16(adw, ADW_MC_IDLE_CMD, cmd);
538	splx(s);
539}
540
541/* Wait for an idle command to complete */
542adw_idle_cmd_status_t
543adw_idle_cmd_wait(struct adw_softc *adw)
544{
545	u_int		      timeout;
546	adw_idle_cmd_status_t status;
547	int		      s;
548
549	/* Wait for up to 10 seconds for the command to complete */
550	timeout = 10000;
551	while (--timeout) {
552       		if (adw->idle_command_cmp != 0)
553			break;
554		DELAY(1000);
555	}
556
557	if (timeout == 0)
558		panic("%s: Idle Command Timed Out!\n", adw_name(adw));
559	s = splcam();
560	status = adw_lram_read_16(adw, ADW_MC_IDLE_PARA_STAT);
561	splx(s);
562	return (status);
563}
564