ata-lowlevel.c revision 119651
1119404Ssos/*-
2119404Ssos * Copyright (c) 1998 - 2003 S�ren Schmidt <sos@FreeBSD.org>
3119404Ssos * All rights reserved.
4119404Ssos *
5119404Ssos * Redistribution and use in source and binary forms, with or without
6119404Ssos * modification, are permitted provided that the following conditions
7119404Ssos * are met:
8119404Ssos * 1. Redistributions of source code must retain the above copyright
9119404Ssos *    notice, this list of conditions and the following disclaimer,
10119404Ssos *    without modification, immediately at the beginning of the file.
11119404Ssos * 2. Redistributions in binary form must reproduce the above copyright
12119404Ssos *    notice, this list of conditions and the following disclaimer in the
13119404Ssos *    documentation and/or other materials provided with the distribution.
14119404Ssos * 3. The name of the author may not be used to endorse or promote products
15119404Ssos *    derived from this software without specific prior written permission.
16119404Ssos *
17119404Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18119404Ssos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19119404Ssos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20119404Ssos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21119404Ssos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22119404Ssos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23119404Ssos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24119404Ssos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25119404Ssos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26119404Ssos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27119404Ssos */
28119404Ssos
29119418Sobrien#include <sys/cdefs.h>
30119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ata/ata-lowlevel.c 119651 2003-09-01 11:13:21Z sos $");
31119418Sobrien
32119404Ssos#include "opt_ata.h"
33119404Ssos#include <sys/param.h>
34119404Ssos#include <sys/systm.h>
35119404Ssos#include <sys/ata.h>
36119404Ssos#include <sys/kernel.h>
37119404Ssos#include <sys/conf.h>
38119404Ssos#include <sys/bus.h>
39119404Ssos#include <sys/mutex.h>
40119404Ssos#include <sys/taskqueue.h>
41119404Ssos#include <machine/bus.h>
42119404Ssos#include <sys/rman.h>
43119404Ssos#include <dev/ata/ata-all.h>
44119404Ssos
45119404Ssos/* prototypes */
46119450Ssosstatic int ata_transaction(struct ata_request *);
47119450Ssosstatic void ata_interrupt(void *);
48119450Ssosstatic void ata_reset(struct ata_channel *);
49119450Ssosstatic int ata_wait(struct ata_device *, u_int8_t);
50119450Ssosstatic int ata_command(struct ata_device *, u_int8_t, u_int64_t, u_int16_t, u_int16_t);
51119450Ssosstatic void ata_pio_read(struct ata_request *, int);
52119450Ssosstatic void ata_pio_write(struct ata_request *, int);
53119404Ssos
54119404Ssos/* local vars */
55119404Ssosstatic int atadebug = 0;
56119404Ssos
57119404Ssos/*
58119404Ssos * low level ATA functions
59119404Ssos */
60119404Ssosvoid
61119404Ssosata_generic_hw(struct ata_channel *ch)
62119404Ssos{
63119404Ssos    ch->hw.reset = ata_reset;
64119404Ssos    ch->hw.transaction = ata_transaction;
65119404Ssos    ch->hw.interrupt = ata_interrupt;
66119404Ssos}
67119404Ssos
68119404Ssos/* must be called with ATA channel locked */
69119404Ssosstatic int
70119404Ssosata_transaction(struct ata_request *request)
71119404Ssos{
72119404Ssos    /* record the request as running */
73119404Ssos    request->device->channel->running = request;
74119404Ssos
75119404Ssos    /* disable ATAPI DMA writes if HW doesn't support it */
76119404Ssos    if (request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_WRITE) &&
77119404Ssos	request->device->channel->flags & ATA_ATAPI_DMA_RO)
78119404Ssos	request->flags &= ~ATA_R_DMA;
79119404Ssos
80119404Ssos    switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA)) {
81119404Ssos
82119404Ssos    /* ATA PIO data transfer and control commands */
83119404Ssos    default:
84119404Ssos	{
85119404Ssos	/* record command direction here as our request might be done later */
86119404Ssos	int write = (request->flags & ATA_R_WRITE);
87119404Ssos
88119404Ssos	    /* issue command */
89119404Ssos	    if (ata_command(request->device, request->u.ata.command,
90119404Ssos			    request->u.ata.lba, request->u.ata.count,
91119404Ssos			    request->u.ata.feature)) {
92119404Ssos		ata_prtdev(request->device, "error issueing PIO command\n");
93119404Ssos		request->result = EIO;
94119404Ssos		return ATA_OP_FINISHED;
95119404Ssos	    }
96119404Ssos
97119404Ssos	    /* if write command output the data */
98119404Ssos	    if (write) {
99119404Ssos		if (ata_wait(request->device,
100119404Ssos			     (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)) < 0) {
101119404Ssos		    ata_prtdev(request->device,"timeout waiting for write DRQ");
102119404Ssos		    request->result = EIO;
103119404Ssos		    return ATA_OP_FINISHED;
104119404Ssos		}
105119404Ssos		ata_pio_write(request, request->transfersize);
106119404Ssos	    }
107119404Ssos	}
108119404Ssos	/* return and wait for interrupt */
109119404Ssos	return ATA_OP_CONTINUES;
110119404Ssos
111119404Ssos    /* ATA DMA data transfer commands */
112119404Ssos    case ATA_R_DMA:
113119404Ssos	/* check sanity and setup DMA engine */
114119404Ssos	if (request->device->channel->dma->setup(request->device,
115119404Ssos						 request->data,
116119404Ssos						 request->bytecount)) {
117119404Ssos	    ata_prtdev(request->device, "setting up DMA failed\n");
118119404Ssos	    request->result = EIO;
119119404Ssos	    return ATA_OP_FINISHED;
120119404Ssos	}
121119404Ssos
122119404Ssos	/* issue command */
123119404Ssos	if (ata_command(request->device, request->u.ata.command,
124119404Ssos			request->u.ata.lba, request->u.ata.count,
125119404Ssos			request->u.ata.feature)) {
126119404Ssos	    ata_prtdev(request->device, "error issuing DMA command\n");
127119404Ssos	    request->result = EIO;
128119404Ssos	    return ATA_OP_FINISHED;
129119404Ssos	}
130119404Ssos
131119404Ssos	/* start DMA engine */
132119404Ssos	if (request->device->channel->dma->start(request->device->channel,
133119404Ssos						 request->data,
134119404Ssos						 request->bytecount,
135119404Ssos						 request->flags & ATA_R_READ)) {
136119404Ssos	    request->result = EIO;
137119404Ssos	    return ATA_OP_FINISHED;
138119404Ssos	}
139119404Ssos	/* return and wait for interrupt */
140119404Ssos	return ATA_OP_CONTINUES;
141119404Ssos
142119404Ssos    /* ATAPI PIO commands */
143119404Ssos    case ATA_R_ATAPI:
144119404Ssos	/* is this just a POLL DSC command ? */
145119404Ssos	if (request->u.atapi.ccb[0] == ATAPI_POLL_DSC) {
146119404Ssos	    ATA_IDX_OUTB(request->device->channel, ATA_DRIVE,
147119404Ssos			 ATA_D_IBM | request->device->unit);
148119404Ssos	    DELAY(10);
149119404Ssos	    if (!(ATA_IDX_INB(request->device->channel, ATA_ALTSTAT)&ATA_S_DSC))
150119404Ssos		request->result = EBUSY;
151119404Ssos	    return ATA_OP_FINISHED;
152119404Ssos	}
153119404Ssos
154119404Ssos	/* start ATAPI operation */
155119404Ssos	if (ata_command(request->device, ATA_PACKET_CMD,
156119404Ssos			request->transfersize << 8, 0, 0)) {
157119404Ssos	    ata_prtdev(request->device, "error issuing ATA PACKET command\n");
158119404Ssos	    request->result = EIO;
159119404Ssos	    return ATA_OP_FINISHED;
160119404Ssos	}
161119404Ssos
162119404Ssos	/* command interrupt device ? just return and wait for interrupt */
163119404Ssos	if ((request->device->param->config & ATA_DRQ_MASK) == ATA_DRQ_INTR)
164119404Ssos	    return ATA_OP_CONTINUES;
165119404Ssos
166119404Ssos	/* wait for ready to write ATAPI command block */
167119404Ssos	{
168119404Ssos	    int timeout = 5000; /* might be less for fast devices */
169119404Ssos	    while (timeout--) {
170119404Ssos		int reason = ATA_IDX_INB(request->device->channel, ATA_IREASON);
171119404Ssos		int status = ATA_IDX_INB(request->device->channel, ATA_STATUS);
172119404Ssos
173119404Ssos		if (((reason & (ATA_I_CMD | ATA_I_IN)) |
174119404Ssos		     (status & (ATA_S_DRQ | ATA_S_BUSY))) == ATAPI_P_CMDOUT)
175119404Ssos		    break;
176119404Ssos		DELAY(20);
177119404Ssos	    }
178119404Ssos	    if (timeout <= 0) {
179119404Ssos		ata_prtdev(request->device,
180119404Ssos			   "timeout waiting for ATAPI ready\n");
181119404Ssos		request->result = EIO;
182119404Ssos		return ATA_OP_FINISHED;
183119404Ssos	    }
184119404Ssos	}
185119404Ssos
186119404Ssos	/* this seems to be needed for some (slow) devices */
187119404Ssos	DELAY(10);
188119404Ssos
189119404Ssos	/* output actual command block */
190119404Ssos	ATA_IDX_OUTSW_STRM(request->device->channel, ATA_DATA,
191119404Ssos			   (int16_t *)request->u.atapi.ccb,
192119404Ssos			   (request->device->param->config & ATA_PROTO_MASK) ==
193119404Ssos			   ATA_PROTO_ATAPI_12 ? 6 : 8);
194119404Ssos
195119404Ssos	/* return and wait for interrupt */
196119404Ssos	return ATA_OP_CONTINUES;
197119404Ssos
198119404Ssos    case ATA_R_ATAPI|ATA_R_DMA:
199119404Ssos	/* is this just a POLL DSC command ? */
200119404Ssos	if (request->u.atapi.ccb[0] == ATAPI_POLL_DSC) {
201119404Ssos	    ATA_IDX_OUTB(request->device->channel, ATA_DRIVE,
202119404Ssos			 ATA_D_IBM | request->device->unit);
203119404Ssos	    DELAY(10);
204119404Ssos	    if (!(ATA_IDX_INB(request->device->channel, ATA_ALTSTAT)&ATA_S_DSC))
205119404Ssos		request->result = EBUSY;
206119404Ssos	    return ATA_OP_FINISHED;
207119404Ssos	}
208119404Ssos
209119404Ssos	/* check sanity and setup DMA engine */
210119404Ssos	if (request->device->channel->dma->setup(request->device,
211119404Ssos						 request->data,
212119404Ssos						 request->bytecount)) {
213119404Ssos	    ata_prtdev(request->device, "setting up DMA failed\n");
214119404Ssos	    request->result = EIO;
215119404Ssos	    return ATA_OP_FINISHED;
216119404Ssos	}
217119404Ssos
218119404Ssos	/* start ATAPI operation */
219119404Ssos	if (ata_command(request->device, ATA_PACKET_CMD, 0, 0, ATA_F_DMA)) {
220119404Ssos	    ata_prtdev(request->device, "error issuing ATAPI packet command\n");
221119404Ssos	    request->result = EIO;
222119404Ssos	    return ATA_OP_FINISHED;
223119404Ssos	}
224119404Ssos
225119404Ssos	/* wait for ready to write ATAPI command block */
226119404Ssos	{
227119404Ssos	    int timeout = 5000; /* might be less for fast devices */
228119404Ssos	    while (timeout--) {
229119404Ssos		int reason = ATA_IDX_INB(request->device->channel, ATA_IREASON);
230119404Ssos		int status = ATA_IDX_INB(request->device->channel, ATA_STATUS);
231119404Ssos
232119404Ssos		if (((reason & (ATA_I_CMD | ATA_I_IN)) |
233119404Ssos		     (status & (ATA_S_DRQ | ATA_S_BUSY))) == ATAPI_P_CMDOUT)
234119404Ssos		    break;
235119404Ssos		DELAY(20);
236119404Ssos	    }
237119404Ssos	    if (timeout <= 0) {
238119404Ssos		ata_prtdev(request->device,
239119404Ssos			   "timeout waiting for ATAPI ready\n");
240119404Ssos		request->result = EIO;
241119404Ssos		return ATA_OP_FINISHED;
242119404Ssos	    }
243119404Ssos	}
244119404Ssos
245119404Ssos	/* this seems to be needed for some (slow) devices */
246119404Ssos	DELAY(10);
247119404Ssos
248119404Ssos	/* output actual command block */
249119404Ssos	ATA_IDX_OUTSW_STRM(request->device->channel, ATA_DATA,
250119404Ssos			   (int16_t *)request->u.atapi.ccb,
251119404Ssos			   (request->device->param->config & ATA_PROTO_MASK) ==
252119404Ssos			   ATA_PROTO_ATAPI_12 ? 6 : 8);
253119404Ssos
254119404Ssos	/* start DMA engine */
255119404Ssos	if (request->device->channel->dma->start(request->device->channel,
256119404Ssos						 request->data,
257119404Ssos						 request->bytecount,
258119404Ssos						 request->flags & ATA_R_READ)) {
259119404Ssos	    request->result = EIO;
260119404Ssos	    return ATA_OP_FINISHED;
261119404Ssos	}
262119404Ssos
263119404Ssos	/* return and wait for interrupt */
264119404Ssos	return ATA_OP_CONTINUES;
265119404Ssos    }
266119404Ssos}
267119404Ssos
268119404Ssosstatic void
269119404Ssosata_interrupt(void *data)
270119404Ssos{
271119404Ssos    struct ata_channel *ch = (struct ata_channel *)data;
272119404Ssos    struct ata_request *request = ch->running;
273119404Ssos    int length;
274119404Ssos
275119404Ssos    /* if we dont have a running request shout and ignore this interrupt */
276119404Ssos    if (request == NULL) {
277119404Ssos	if (bootverbose) {
278119404Ssos	    printf("ata%d: spurious interrupt - ", device_get_unit(ch->dev));
279119404Ssos	    if (request)
280119404Ssos		printf("request OK - ");
281119404Ssos	    printf("status=0x%02x error=0x%02x reason=0x%02x\n",
282119404Ssos		   ATA_IDX_INB(ch, ATA_ALTSTAT), ATA_IDX_INB(ch, ATA_ERROR),
283119404Ssos		   ATA_IDX_INB(ch, ATA_IREASON));
284119404Ssos	}
285119404Ssos	return;
286119404Ssos    }
287119404Ssos
288119404Ssos    /* if device is busy it didn't interrupt */
289119404Ssos    if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
290119404Ssos	DELAY(100);
291119404Ssos	if (!(ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_DRQ))
292119404Ssos	    return;
293119404Ssos    }
294119404Ssos
295119404Ssos    /* clear interrupt and get status */
296119404Ssos    request->status = ATA_IDX_INB(ch, ATA_STATUS);
297119404Ssos
298119404Ssos    switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA)) {
299119404Ssos
300119404Ssos    /* ATA PIO data transfer and control commands */
301119404Ssos    default:
302119404Ssos
303119404Ssos	/* if we got an error we are done with the HW */
304119404Ssos	if (request->status & ATA_S_ERROR) {
305119404Ssos	    request->error = ATA_IDX_INB(ch, ATA_ERROR);
306119404Ssos	    break;
307119404Ssos	}
308119404Ssos
309119404Ssos	/* if read data get it */
310119404Ssos	if (request->flags & ATA_R_READ)
311119404Ssos	    ata_pio_read(request, request->transfersize);
312119404Ssos
313119404Ssos	/* update how far we've gotten */
314119404Ssos	request->donecount += request->transfersize;
315119404Ssos
316119404Ssos	/* do we need a scoop more ? */
317119404Ssos	if (request->bytecount > request->donecount) {
318119404Ssos
319119404Ssos	    /* set this transfer size according to HW capabilities */
320119404Ssos	    request->transfersize =
321119424Ssos		min((request->bytecount - request->donecount),
322119404Ssos		    request->transfersize);
323119404Ssos
324119404Ssos	    /* if data write command, output the data */
325119404Ssos	    if (request->flags & ATA_R_WRITE) {
326119404Ssos		/* if we get an error here we are done with the HW */
327119404Ssos		if (ata_wait(request->device,
328119404Ssos			     (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)) < 0) {
329119404Ssos		    ata_prtdev(request->device,"timeout waiting for write DRQ");
330119404Ssos		    request->status = ATA_IDX_INB(ch, ATA_STATUS);
331119404Ssos		    break;
332119404Ssos		}
333119404Ssos		else {
334119404Ssos		    /* output data and return waiting for new interrupt */
335119404Ssos		    ata_pio_write(request, request->transfersize);
336119404Ssos		    return;
337119404Ssos		}
338119404Ssos	    }
339119404Ssos
340119404Ssos	    /* if data read command, return & wait for interrupt */
341119404Ssos	    else if (request->flags & ATA_R_READ) {
342119404Ssos		return;
343119404Ssos	    }
344119404Ssos	    else
345119404Ssos		ata_prtdev(request->device,
346119404Ssos			   "FAILURE - %s shouldn't loop on control cmd\n",
347119404Ssos			   ata_cmd2str(request));
348119404Ssos	}
349119404Ssos	/* done with HW */
350119404Ssos	break;
351119404Ssos
352119404Ssos    /* ATA DMA data transfer commands */
353119404Ssos    case ATA_R_DMA:
354119404Ssos	/* stop DMA engine and get status */
355119404Ssos	request->dmastat = ch->dma->stop(ch);
356119404Ssos
357119404Ssos	/* did we get error or data */
358119404Ssos	if (request->status & ATA_S_ERROR)
359119404Ssos	    request->error = ATA_IDX_INB(ch, ATA_ERROR);
360119404Ssos	else if (request->dmastat & ATA_BMSTAT_ERROR)
361119404Ssos	    request->status |= ATA_S_ERROR;
362119404Ssos	else
363119404Ssos	    request->donecount = request->bytecount;
364119404Ssos
365119404Ssos	/* done with HW */
366119404Ssos	break;
367119404Ssos
368119404Ssos    /* ATAPI PIO commands */
369119404Ssos    case ATA_R_ATAPI:
370119404Ssos	length = ATA_IDX_INB(ch, ATA_CYL_LSB)|(ATA_IDX_INB(ch, ATA_CYL_MSB)<<8);
371119404Ssos
372119404Ssos	switch ((ATA_IDX_INB(ch, ATA_IREASON) & (ATA_I_CMD | ATA_I_IN)) |
373119404Ssos		(request->status & ATA_S_DRQ)) {
374119404Ssos
375119404Ssos	case ATAPI_P_CMDOUT:
376119404Ssos	    /* this seems to be needed for some (slow) devices */
377119404Ssos	    DELAY(10);
378119404Ssos
379119404Ssos	    if (!(request->status & ATA_S_DRQ)) {
380119404Ssos		ata_prtdev(request->device, "command interrupt without DRQ\n");
381119404Ssos		request->status = ATA_S_ERROR;
382119404Ssos		break;
383119404Ssos	    }
384119404Ssos	    ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (int16_t *)request->u.atapi.ccb,
385119404Ssos			       (request->device->param->config &
386119404Ssos				ATA_PROTO_MASK)== ATA_PROTO_ATAPI_12 ? 6 : 8);
387119404Ssos	    /* return wait for interrupt */
388119404Ssos	    return;
389119404Ssos
390119404Ssos	case ATAPI_P_WRITE:
391119404Ssos	    if (request->flags & ATA_R_READ) {
392119404Ssos		request->status = ATA_S_ERROR;
393119404Ssos		ata_prtdev(request->device,
394119404Ssos			   "%s trying to write on read buffer\n",
395119404Ssos			   ata_cmd2str(request));
396119404Ssos		break;
397119404Ssos	    }
398119404Ssos	    ata_pio_write(request, length);
399119404Ssos	    request->donecount += length;
400119404Ssos
401119404Ssos	    /* set next transfer size according to HW capabilities */
402119404Ssos	    request->transfersize = min((request->bytecount-request->donecount),
403119404Ssos					request->transfersize);
404119404Ssos	    /* return wait for interrupt */
405119404Ssos	    return;
406119404Ssos
407119404Ssos	case ATAPI_P_READ:
408119404Ssos	    if (request->flags & ATA_R_WRITE) {
409119404Ssos		request->status = ATA_S_ERROR;
410119404Ssos		ata_prtdev(request->device,
411119404Ssos			   "%s trying to read on write buffer\n",
412119404Ssos			   ata_cmd2str(request));
413119404Ssos		break;
414119404Ssos	    }
415119404Ssos	    ata_pio_read(request, length);
416119404Ssos	    request->donecount += length;
417119404Ssos
418119404Ssos	    /* set next transfer size according to HW capabilities */
419119404Ssos	    request->transfersize = min((request->bytecount-request->donecount),
420119404Ssos					request->transfersize);
421119404Ssos	    /* return wait for interrupt */
422119404Ssos	    return;
423119404Ssos
424119404Ssos	case ATAPI_P_DONEDRQ:
425119404Ssos	    ata_prtdev(request->device,
426119404Ssos		       "WARNING - %s DONEDRQ non conformant device\n",
427119404Ssos		       ata_cmd2str(request));
428119404Ssos	    if (request->flags & ATA_R_READ) {
429119404Ssos		ata_pio_read(request, length);
430119404Ssos		request->donecount += length;
431119404Ssos	    }
432119404Ssos	    else if (request->flags & ATA_R_WRITE) {
433119404Ssos		ata_pio_write(request, length);
434119404Ssos		request->donecount += length;
435119404Ssos	    }
436119404Ssos	    else
437119404Ssos		request->status = ATA_S_ERROR;
438119404Ssos	    /* FALLTHROUGH */
439119404Ssos
440119404Ssos	case ATAPI_P_ABORT:
441119404Ssos	case ATAPI_P_DONE:
442119404Ssos	    if (request->status & (ATA_S_ERROR | ATA_S_DWF))
443119404Ssos		request->error = ATA_IDX_INB(ch, ATA_ERROR);
444119404Ssos	    break;
445119404Ssos
446119404Ssos	default:
447119404Ssos	    ata_prtdev(request->device, "unknown transfer phase\n");
448119404Ssos	    request->status = ATA_S_ERROR;
449119404Ssos	}
450119404Ssos	/* done with HW */
451119404Ssos	break;
452119404Ssos
453119404Ssos    /* ATAPI DMA commands */
454119404Ssos    case ATA_R_ATAPI|ATA_R_DMA:
455119404Ssos
456119404Ssos	/* stop the engine and get engine status */
457119404Ssos	request->dmastat = ch->dma->stop(ch);
458119404Ssos
459119404Ssos	/* did we get error or data */
460119404Ssos	if (request->status & (ATA_S_ERROR | ATA_S_DWF))
461119404Ssos	    request->error = ATA_IDX_INB(ch, ATA_ERROR);
462119404Ssos	else if (request->dmastat & ATA_BMSTAT_ERROR)
463119404Ssos	    request->status |= ATA_S_ERROR;
464119404Ssos	else
465119404Ssos	    request->donecount = request->bytecount;
466119404Ssos
467119404Ssos	/* done with HW */
468119404Ssos	break;
469119404Ssos    }
470119404Ssos
471119404Ssos    ata_finish(request);
472119404Ssos
473119404Ssos    /* unlock the ATA HW for new work */
474119404Ssos    ch->running = NULL;
475119404Ssos    ATA_UNLOCK_CH(ch);
476119404Ssos    ch->locking(ch, ATA_LF_UNLOCK);
477119404Ssos}
478119404Ssos
479119404Ssos/* must be called with ATA channel locked */
480119404Ssosstatic void
481119404Ssosata_reset(struct ata_channel *ch)
482119404Ssos{
483119651Ssos    u_int8_t err, lsb, msb, ostat0, ostat1;
484119404Ssos    u_int8_t stat0 = 0, stat1 = 0;
485119404Ssos    int mask = 0, timeout;
486119404Ssos
487119404Ssos    /* do we have any signs of ATA/ATAPI HW being present ? */
488119404Ssos    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
489119404Ssos    DELAY(10);
490119404Ssos    ostat0 = ATA_IDX_INB(ch, ATA_STATUS);
491119404Ssos    if ((ostat0 & 0xf8) != 0xf8 && ostat0 != 0xa5) {
492119404Ssos	stat0 = ATA_S_BUSY;
493119404Ssos	mask |= 0x01;
494119404Ssos    }
495119404Ssos
496119404Ssos    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
497119404Ssos    DELAY(10);
498119404Ssos    ostat1 = ATA_IDX_INB(ch, ATA_STATUS);
499119651Ssos
500119404Ssos    /* in some setups we dont want to test for a slave */
501119404Ssos    if (!(ch->flags & ATA_NO_SLAVE)) {
502119404Ssos	if ((ostat1 & 0xf8) != 0xf8 && ostat1 != 0xa5) {
503119404Ssos	    stat1 = ATA_S_BUSY;
504119404Ssos	    mask |= 0x02;
505119404Ssos	}
506119404Ssos    }
507119404Ssos
508119404Ssos    /* if nothing showed up no need to get any further */
509119404Ssos    /* SOS is that too strong?, we just might loose devices here XXX */
510119404Ssos    ch->devices = 0;
511119404Ssos    if (!mask)
512119404Ssos	return;
513119404Ssos
514119404Ssos    if (bootverbose)
515119651Ssos	ata_printf(ch, -1, "reset tp1 mask=%02x ostat0=%02x ostat1=%02x\n",
516119404Ssos		   mask, ostat0, ostat1);
517119404Ssos
518119404Ssos    /* reset channel */
519119404Ssos    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
520119404Ssos    DELAY(10);
521119404Ssos    ATA_IDX_OUTB(ch, ATA_ALTSTAT, ATA_A_IDS | ATA_A_RESET);
522119404Ssos    DELAY(10000);
523119404Ssos    ATA_IDX_OUTB(ch, ATA_ALTSTAT, ATA_A_IDS);
524119501Ssos    DELAY(10000);
525119404Ssos
526119404Ssos    /* wait for BUSY to go inactive */
527119501Ssos    for (timeout = 0; timeout < 310; timeout++) {
528119404Ssos	if (stat0 & ATA_S_BUSY) {
529119404Ssos	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
530119404Ssos	    DELAY(10);
531119651Ssos    	    err = ATA_IDX_INB(ch, ATA_ERROR);
532119651Ssos	    lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
533119651Ssos	    msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
534119404Ssos	    stat0 = ATA_IDX_INB(ch, ATA_STATUS);
535119651Ssos	    if (bootverbose)
536119651Ssos		ata_printf(ch, ATA_MASTER,
537119651Ssos			   "stat=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
538119651Ssos			   stat0, err, lsb, msb);
539119651Ssos	    if (!(stat0 & ATA_S_BUSY) && err == ATA_E_ILI) {
540119651Ssos		if (stat0 & ATA_S_READY) {
541119651Ssos		    ch->devices |= ATA_ATA_MASTER;
542119651Ssos		}
543119651Ssos		else if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) {
544119404Ssos		    ch->devices |= ATA_ATAPI_MASTER;
545119523Ssos		}
546119404Ssos	    }
547119404Ssos	}
548119404Ssos	if (stat1 & ATA_S_BUSY) {
549119404Ssos	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
550119404Ssos	    DELAY(10);
551119651Ssos    	    err = ATA_IDX_INB(ch, ATA_ERROR);
552119651Ssos	    lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
553119651Ssos	    msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
554119404Ssos	    stat1 = ATA_IDX_INB(ch, ATA_STATUS);
555119651Ssos	    if (bootverbose)
556119651Ssos		ata_printf(ch, ATA_SLAVE,
557119651Ssos			   "stat=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
558119651Ssos			   stat0, err, lsb, msb);
559119651Ssos	    if (!(stat1 & ATA_S_BUSY) && err == ATA_E_ILI) {
560119651Ssos		if (stat1 & ATA_S_READY) {
561119651Ssos		    ch->devices |= ATA_ATA_SLAVE;
562119651Ssos		}
563119651Ssos		else if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) {
564119404Ssos		    ch->devices |= ATA_ATAPI_SLAVE;
565119523Ssos		}
566119404Ssos	    }
567119404Ssos	}
568119404Ssos	if (mask == 0x01)      /* wait for master only */
569119523Ssos	    if (!(stat0 & ATA_S_BUSY) || (stat0 == 0xff && timeout > 20))
570119404Ssos		break;
571119404Ssos	if (mask == 0x02)      /* wait for slave only */
572119523Ssos	    if (!(stat1 & ATA_S_BUSY) || (stat1 == 0xff && timeout > 20))
573119404Ssos		break;
574119404Ssos	if (mask == 0x03)      /* wait for both master & slave */
575119651Ssos	    if ((!(stat0 & ATA_S_BUSY) || (stat0 == 0xff && timeout > 20)) &&
576119651Ssos	        (!(stat1 & ATA_S_BUSY) || (stat1 == 0xff && timeout > 20)))
577119404Ssos		break;
578119501Ssos	DELAY(100000);
579119404Ssos    }
580119404Ssos
581119404Ssos    if (stat0 & ATA_S_BUSY)
582119404Ssos	mask &= ~0x01;
583119404Ssos    if (stat1 & ATA_S_BUSY)
584119404Ssos	mask &= ~0x02;
585119651Ssos
586119404Ssos    if (bootverbose)
587119651Ssos	ata_printf(ch, -1,
588119651Ssos		   "reset tp2 mask=%02x stat0=%02x stat1=%02x devices=0x%b\n",
589119651Ssos		   mask, stat0, stat1, ch->devices,
590119651Ssos		   "\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
591119404Ssos    if (!mask)
592119404Ssos	return;
593119404Ssos
594119651Ssos    if (mask & 0x01 && ostat0 != 0x00 &&
595119651Ssos        !(ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER))) {
596119404Ssos	ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
597119404Ssos	DELAY(10);
598119404Ssos	ATA_IDX_OUTB(ch, ATA_ERROR, 0x58);
599119404Ssos	ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0xa5);
600119651Ssos	err = ATA_IDX_INB(ch, ATA_ERROR);
601119651Ssos	lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
602119404Ssos	if (bootverbose)
603119651Ssos	    ata_printf(ch, ATA_MASTER, "ATA err=0x%02x lsb=0x%02x\n", err, lsb);
604119651Ssos	if (err != 0x58 && lsb == 0xa5)
605119404Ssos	    ch->devices |= ATA_ATA_MASTER;
606119404Ssos    }
607119651Ssos    if (mask & 0x02 && ostat1 != 0x00 &&
608119651Ssos	!(ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE))) {
609119404Ssos	ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
610119404Ssos	DELAY(10);
611119404Ssos	ATA_IDX_OUTB(ch, ATA_ERROR, 0x58);
612119404Ssos	ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0xa5);
613119651Ssos	err = ATA_IDX_INB(ch, ATA_ERROR);
614119651Ssos	lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
615119404Ssos	if (bootverbose)
616119651Ssos	    ata_printf(ch, ATA_SLAVE, "ATA err=0x%02x lsb=0x%02x\n", err, lsb);
617119651Ssos	if (err != 0x58 && lsb == 0xa5)
618119404Ssos	    ch->devices |= ATA_ATA_SLAVE;
619119404Ssos    }
620119651Ssos
621119404Ssos    if (bootverbose)
622119651Ssos	ata_printf(ch, -1, "reset tp3 devices=0x%b\n", ch->devices,
623119651Ssos		   "\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
624119404Ssos}
625119404Ssos
626119404Ssosstatic int
627119404Ssosata_wait(struct ata_device *atadev, u_int8_t mask)
628119404Ssos{
629119404Ssos    int timeout = 0;
630119404Ssos    u_int8_t status;
631119404Ssos
632119404Ssos    DELAY(1);
633119404Ssos    while (timeout < 5000000) { /* timeout 5 secs */
634119404Ssos	status = ATA_IDX_INB(atadev->channel, ATA_STATUS);
635119404Ssos
636119404Ssos	/* if drive fails status, reselect the drive just to be sure */
637119404Ssos	if (status == 0xff) {
638119404Ssos	    ata_prtdev(atadev, "WARNING no status, reselecting device\n");
639119404Ssos	    ATA_IDX_OUTB(atadev->channel, ATA_DRIVE, ATA_D_IBM | atadev->unit);
640119404Ssos	    DELAY(10);
641119404Ssos	    status = ATA_IDX_INB(atadev->channel, ATA_STATUS);
642119404Ssos	    if (status == 0xff)
643119404Ssos		return -1;
644119404Ssos	}
645119404Ssos
646119404Ssos	/* are we done ? */
647119404Ssos	if (!(status & ATA_S_BUSY))
648119404Ssos	    break;
649119404Ssos
650119404Ssos	if (timeout > 1000) {
651119404Ssos	    timeout += 1000;
652119404Ssos	    DELAY(1000);
653119404Ssos	}
654119404Ssos	else {
655119404Ssos	    timeout += 10;
656119404Ssos	    DELAY(10);
657119404Ssos	}
658119404Ssos    }
659119404Ssos    if (timeout >= 5000000)
660119404Ssos	return -1;
661119404Ssos    if (!mask)
662119404Ssos	return (status & ATA_S_ERROR);
663119404Ssos
664119404Ssos    /* wait 50 msec for bits wanted. */
665119404Ssos    timeout = 5000;
666119404Ssos    while (timeout--) {
667119404Ssos	status = ATA_IDX_INB(atadev->channel, ATA_STATUS);
668119404Ssos	if ((status & mask) == mask)
669119404Ssos	    return (status & ATA_S_ERROR);
670119404Ssos	DELAY (10);
671119404Ssos    }
672119404Ssos    return -1;
673119404Ssos}
674119404Ssos
675119404Ssosstatic int
676119404Ssosata_command(struct ata_device *atadev, u_int8_t command,
677119404Ssos	    u_int64_t lba, u_int16_t count, u_int16_t feature)
678119404Ssos{
679119404Ssos    if (atadebug)
680119404Ssos	ata_prtdev(atadev, "ata_command: addr=%04lx, command=%02x, "
681119404Ssos		   "lba=%jd, count=%d, feature=%d\n",
682119404Ssos		   rman_get_start(atadev->channel->r_io[ATA_DATA].res),
683119404Ssos		   command, (intmax_t)lba, count, feature);
684119404Ssos
685119404Ssos    /* select device */
686119404Ssos    ATA_IDX_OUTB(atadev->channel, ATA_DRIVE, ATA_D_IBM | atadev->unit);
687119404Ssos
688119404Ssos    /* ready to issue command ? */
689119404Ssos    if (ata_wait(atadev, 0) < 0) {
690119404Ssos	ata_prtdev(atadev, "timeout sending command=%02x\n", command);
691119404Ssos	return -1;
692119404Ssos    }
693119404Ssos
694119651Ssos    /* enable interrupt */
695119651Ssos    ATA_IDX_OUTB(atadev->channel, ATA_ALTSTAT, ATA_A_4BIT);
696119651Ssos
697119404Ssos    /* only use 48bit addressing if needed (avoid bugs and overhead) */
698119404Ssos    if ((lba > 268435455 || count > 256) && atadev->param &&
699119404Ssos	atadev->param->support.command2 & ATA_SUPPORT_ADDRESS48) {
700119404Ssos
701119404Ssos	/* translate command into 48bit version */
702119404Ssos	switch (command) {
703119404Ssos	case ATA_READ:
704119404Ssos	    command = ATA_READ48; break;
705119404Ssos	case ATA_READ_MUL:
706119404Ssos	    command = ATA_READ_MUL48; break;
707119404Ssos	case ATA_READ_DMA:
708119404Ssos	    command = ATA_READ_DMA48; break;
709119404Ssos	case ATA_READ_DMA_QUEUED:
710119404Ssos	    command = ATA_READ_DMA_QUEUED48; break;
711119404Ssos	case ATA_WRITE:
712119404Ssos	    command = ATA_WRITE48; break;
713119404Ssos	case ATA_WRITE_MUL:
714119404Ssos	    command = ATA_WRITE_MUL48; break;
715119404Ssos	case ATA_WRITE_DMA:
716119404Ssos	    command = ATA_WRITE_DMA48; break;
717119404Ssos	case ATA_WRITE_DMA_QUEUED:
718119404Ssos	    command = ATA_WRITE_DMA_QUEUED48; break;
719119404Ssos	case ATA_FLUSHCACHE:
720119404Ssos	    command = ATA_FLUSHCACHE48; break;
721119404Ssos	default:
722119404Ssos	    ata_prtdev(atadev, "can't translate cmd to 48bit version\n");
723119404Ssos	    return -1;
724119404Ssos	}
725119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_FEATURE, (feature>>8) & 0xff);
726119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_FEATURE, feature & 0xff);
727119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_COUNT, (count>>8) & 0xff);
728119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_COUNT, count & 0xff);
729119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_SECTOR, (lba>>24) & 0xff);
730119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_SECTOR, lba & 0xff);
731119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_CYL_LSB, (lba>>32) & 0xff);
732119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_CYL_LSB, (lba>>8) & 0xff);
733119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_CYL_MSB, (lba>>40) & 0xff);
734119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_CYL_MSB, (lba>>16) & 0xff);
735119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_DRIVE, ATA_D_LBA | atadev->unit);
736119404Ssos	atadev->channel->flags |= ATA_48BIT_ACTIVE;
737119404Ssos    }
738119404Ssos    else {
739119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_FEATURE, feature);
740119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_COUNT, count);
741119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_SECTOR, lba & 0xff);
742119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_CYL_LSB, (lba>>8) & 0xff);
743119404Ssos	ATA_IDX_OUTB(atadev->channel, ATA_CYL_MSB, (lba>>16) & 0xff);
744119404Ssos	if (atadev->flags & ATA_D_USE_CHS)
745119404Ssos	    ATA_IDX_OUTB(atadev->channel, ATA_DRIVE,
746119404Ssos			 ATA_D_IBM | atadev->unit | ((lba>>24) & 0xf));
747119404Ssos	else
748119404Ssos	    ATA_IDX_OUTB(atadev->channel, ATA_DRIVE,
749119404Ssos			 ATA_D_IBM | ATA_D_LBA | atadev->unit|((lba>>24)&0xf));
750119404Ssos	atadev->channel->flags &= ~ATA_48BIT_ACTIVE;
751119404Ssos    }
752119404Ssos
753119404Ssos    /* issue command to controller */
754119404Ssos    ATA_IDX_OUTB(atadev->channel, ATA_CMD, command);
755119404Ssos
756119404Ssos    return 0;
757119404Ssos}
758119404Ssos
759119404Ssosstatic void
760119404Ssosata_pio_read(struct ata_request *request, int length)
761119404Ssos{
762119404Ssos    int size = min(request->transfersize, length);
763119404Ssos    struct ata_channel *ch = request->device->channel;
764119404Ssos    int resid;
765119404Ssos
766119404Ssos    if (ch->flags & ATA_USE_16BIT || (size % sizeof(int32_t)))
767119404Ssos	ATA_IDX_INSW_STRM(ch, ATA_DATA,
768119404Ssos			  (void*)((uintptr_t)request->data+request->donecount),
769119404Ssos			  size / sizeof(int16_t));
770119404Ssos    else
771119404Ssos	ATA_IDX_INSL_STRM(ch, ATA_DATA,
772119404Ssos			  (void*)((uintptr_t)request->data+request->donecount),
773119404Ssos			  size / sizeof(int32_t));
774119404Ssos
775119404Ssos    if (request->transfersize < length) {
776119523Ssos	ata_prtdev(request->device, "WARNING - %s read data overrun %d>%d\n",
777119404Ssos		   ata_cmd2str(request), length, request->transfersize);
778119404Ssos	for (resid = request->transfersize; resid < length;
779119404Ssos	     resid += sizeof(int16_t))
780119404Ssos	    ATA_IDX_INW(ch, ATA_DATA);
781119404Ssos    }
782119404Ssos}
783119404Ssos
784119404Ssosstatic void
785119404Ssosata_pio_write(struct ata_request *request, int length)
786119404Ssos{
787119404Ssos    int size = min(request->transfersize, length);
788119404Ssos    struct ata_channel *ch = request->device->channel;
789119404Ssos    int resid;
790119404Ssos
791119404Ssos    if (ch->flags & ATA_USE_16BIT || (size % sizeof(int32_t)))
792119404Ssos	ATA_IDX_OUTSW_STRM(ch, ATA_DATA,
793119404Ssos			   (void*)((uintptr_t)request->data+request->donecount),
794119404Ssos			   size / sizeof(int16_t));
795119404Ssos    else
796119404Ssos	ATA_IDX_OUTSL_STRM(ch, ATA_DATA,
797119404Ssos			   (void*)((uintptr_t)request->data+request->donecount),
798119404Ssos			   size / sizeof(int32_t));
799119404Ssos
800119404Ssos    if (request->transfersize < length) {
801119523Ssos	ata_prtdev(request->device, "WARNING - %s write data underrun %d>%d\n",
802119404Ssos		   ata_cmd2str(request), length, request->transfersize);
803119404Ssos	for (resid = request->transfersize; resid < length;
804119404Ssos	     resid += sizeof(int16_t))
805119404Ssos	    ATA_IDX_OUTW(ch, ATA_DATA, 0);
806119404Ssos    }
807119404Ssos}
808