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