xref: /dflybsd-src/sys/dev/disk/nata/ata-lowlevel.c (revision eb67213abec698ffb555ee926f2761bcb7e95f55)
1c1b3d7c5SThomas E. Spanjaard /*-
2f6e8a0a1SImre Vadasz  * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
3c1b3d7c5SThomas E. Spanjaard  * All rights reserved.
4c1b3d7c5SThomas E. Spanjaard  *
5c1b3d7c5SThomas E. Spanjaard  * Redistribution and use in source and binary forms, with or without
6c1b3d7c5SThomas E. Spanjaard  * modification, are permitted provided that the following conditions
7c1b3d7c5SThomas E. Spanjaard  * are met:
8c1b3d7c5SThomas E. Spanjaard  * 1. Redistributions of source code must retain the above copyright
9c1b3d7c5SThomas E. Spanjaard  *    notice, this list of conditions and the following disclaimer,
10c1b3d7c5SThomas E. Spanjaard  *    without modification, immediately at the beginning of the file.
11c1b3d7c5SThomas E. Spanjaard  * 2. Redistributions in binary form must reproduce the above copyright
12c1b3d7c5SThomas E. Spanjaard  *    notice, this list of conditions and the following disclaimer in the
13c1b3d7c5SThomas E. Spanjaard  *    documentation and/or other materials provided with the distribution.
14c1b3d7c5SThomas E. Spanjaard  *
15c1b3d7c5SThomas E. Spanjaard  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16c1b3d7c5SThomas E. Spanjaard  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17c1b3d7c5SThomas E. Spanjaard  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18c1b3d7c5SThomas E. Spanjaard  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19c1b3d7c5SThomas E. Spanjaard  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20c1b3d7c5SThomas E. Spanjaard  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21c1b3d7c5SThomas E. Spanjaard  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22c1b3d7c5SThomas E. Spanjaard  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23c1b3d7c5SThomas E. Spanjaard  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24c1b3d7c5SThomas E. Spanjaard  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25c1b3d7c5SThomas E. Spanjaard  *
26c1b3d7c5SThomas E. Spanjaard  * $FreeBSD: src/sys/dev/ata/ata-lowlevel.c,v 1.77 2006/07/04 20:36:03 sos Exp $
27c1b3d7c5SThomas E. Spanjaard  */
28c1b3d7c5SThomas E. Spanjaard 
29c1b3d7c5SThomas E. Spanjaard #include "opt_ata.h"
30c1b3d7c5SThomas E. Spanjaard 
31c1b3d7c5SThomas E. Spanjaard #include <sys/param.h>
32c1b3d7c5SThomas E. Spanjaard #include <sys/bus.h>
33c1b3d7c5SThomas E. Spanjaard #include <sys/callout.h>
34c1b3d7c5SThomas E. Spanjaard #include <sys/libkern.h>
35c1b3d7c5SThomas E. Spanjaard #include <sys/nata.h>
36c1b3d7c5SThomas E. Spanjaard #include <sys/systm.h>
37c1b3d7c5SThomas E. Spanjaard 
38c1b3d7c5SThomas E. Spanjaard #include "ata-all.h"
39c1b3d7c5SThomas E. Spanjaard #include "ata_if.h"
40c1b3d7c5SThomas E. Spanjaard 
41c1b3d7c5SThomas E. Spanjaard /* prototypes */
42c1b3d7c5SThomas E. Spanjaard static int ata_generic_status(device_t dev);
43c1b3d7c5SThomas E. Spanjaard static int ata_wait(struct ata_channel *ch, struct ata_device *, u_int8_t);
44c1b3d7c5SThomas E. Spanjaard static void ata_pio_read(struct ata_request *, int);
45c1b3d7c5SThomas E. Spanjaard static void ata_pio_write(struct ata_request *, int);
46c04da965Szrj static void ata_tf_read(struct ata_request *);
47c04da965Szrj static void ata_tf_write(struct ata_request *);
48c1b3d7c5SThomas E. Spanjaard 
49c1b3d7c5SThomas E. Spanjaard /*
50c1b3d7c5SThomas E. Spanjaard  * low level ATA functions
51c1b3d7c5SThomas E. Spanjaard  */
52c1b3d7c5SThomas E. Spanjaard void
ata_generic_hw(device_t dev)53c1b3d7c5SThomas E. Spanjaard ata_generic_hw(device_t dev)
54c1b3d7c5SThomas E. Spanjaard {
55c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(dev);
56c1b3d7c5SThomas E. Spanjaard 
57c1b3d7c5SThomas E. Spanjaard     ch->hw.begin_transaction = ata_begin_transaction;
58c1b3d7c5SThomas E. Spanjaard     ch->hw.end_transaction = ata_end_transaction;
59c1b3d7c5SThomas E. Spanjaard     ch->hw.status = ata_generic_status;
60c1b3d7c5SThomas E. Spanjaard     ch->hw.command = ata_generic_command;
6137c16061Szrj     ch->hw.tf_read = ata_tf_read;
6237c16061Szrj     ch->hw.tf_write = ata_tf_write;
63c1b3d7c5SThomas E. Spanjaard }
64c1b3d7c5SThomas E. Spanjaard 
65c1b3d7c5SThomas E. Spanjaard /* must be called with ATA channel locked and state_mtx held */
66c1b3d7c5SThomas E. Spanjaard int
ata_begin_transaction(struct ata_request * request)67c1b3d7c5SThomas E. Spanjaard ata_begin_transaction(struct ata_request *request)
68c1b3d7c5SThomas E. Spanjaard {
69c04da965Szrj     struct ata_channel *ch = device_get_softc(request->parent);
70c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(request->dev);
71c1b3d7c5SThomas E. Spanjaard     int dummy, error;
72c1b3d7c5SThomas E. Spanjaard 
73c1b3d7c5SThomas E. Spanjaard     ATA_DEBUG_RQ(request, "begin transaction");
74c1b3d7c5SThomas E. Spanjaard 
75c1b3d7c5SThomas E. Spanjaard     /* disable ATAPI DMA writes if HW doesn't support it */
76c1b3d7c5SThomas E. Spanjaard     if ((ch->flags & ATA_ATAPI_DMA_RO) &&
77c1b3d7c5SThomas E. Spanjaard 	((request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_WRITE)) ==
78c1b3d7c5SThomas E. Spanjaard 	 (ATA_R_ATAPI | ATA_R_DMA | ATA_R_WRITE)))
79c1b3d7c5SThomas E. Spanjaard 	request->flags &= ~ATA_R_DMA;
80c1b3d7c5SThomas E. Spanjaard 
81c1b3d7c5SThomas E. Spanjaard     /* check for 48 bit access and convert if needed */
82c1b3d7c5SThomas E. Spanjaard     ata_modify_if_48bit(request);
83c1b3d7c5SThomas E. Spanjaard 
84c1b3d7c5SThomas E. Spanjaard     switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA)) {
85c1b3d7c5SThomas E. Spanjaard 
86c1b3d7c5SThomas E. Spanjaard     /* ATA PIO data transfer and control commands */
87c1b3d7c5SThomas E. Spanjaard     default:
88c1b3d7c5SThomas E. Spanjaard 	{
89c1b3d7c5SThomas E. Spanjaard 	/* record command direction here as our request might be gone later */
90c1b3d7c5SThomas E. Spanjaard 	int write = (request->flags & ATA_R_WRITE);
91c1b3d7c5SThomas E. Spanjaard 
92c1b3d7c5SThomas E. Spanjaard 	    /* issue command */
93c1b3d7c5SThomas E. Spanjaard 	    if (ch->hw.command(request)) {
94c1b3d7c5SThomas E. Spanjaard 		device_printf(request->dev, "error issuing %s command\n",
95c1b3d7c5SThomas E. Spanjaard 			   ata_cmd2str(request));
96c1b3d7c5SThomas E. Spanjaard 		request->result = EIO;
97c1b3d7c5SThomas E. Spanjaard 		goto begin_finished;
98c1b3d7c5SThomas E. Spanjaard 	    }
99c1b3d7c5SThomas E. Spanjaard 
100c1b3d7c5SThomas E. Spanjaard 	    /* device reset doesn't interrupt */
101c1b3d7c5SThomas E. Spanjaard 	    if (request->u.ata.command == ATA_DEVICE_RESET) {
102c1b3d7c5SThomas E. Spanjaard 		int timeout = 1000000;
103c1b3d7c5SThomas E. Spanjaard 		do {
104c1b3d7c5SThomas E. Spanjaard 		    DELAY(10);
105c1b3d7c5SThomas E. Spanjaard 		    request->status = ATA_IDX_INB(ch, ATA_STATUS);
106c1b3d7c5SThomas E. Spanjaard 		} while (request->status & ATA_S_BUSY && timeout--);
107c1b3d7c5SThomas E. Spanjaard 		if (request->status & ATA_S_ERROR)
108c1b3d7c5SThomas E. Spanjaard 		    request->error = ATA_IDX_INB(ch, ATA_ERROR);
109c1b3d7c5SThomas E. Spanjaard 		goto begin_finished;
110c1b3d7c5SThomas E. Spanjaard 	    }
111c1b3d7c5SThomas E. Spanjaard 
112c1b3d7c5SThomas E. Spanjaard 	    /* if write command output the data */
113c1b3d7c5SThomas E. Spanjaard 	    if (write) {
114c1b3d7c5SThomas E. Spanjaard 		if (ata_wait(ch, atadev, (ATA_S_READY | ATA_S_DRQ)) < 0) {
115c1b3d7c5SThomas E. Spanjaard 		    device_printf(request->dev,
116c1b3d7c5SThomas E. Spanjaard 				  "timeout waiting for write DRQ\n");
117c1b3d7c5SThomas E. Spanjaard 		    request->result = EIO;
118c1b3d7c5SThomas E. Spanjaard 		    goto begin_finished;
119c1b3d7c5SThomas E. Spanjaard 		}
120c1b3d7c5SThomas E. Spanjaard 		ata_pio_write(request, request->transfersize);
121c1b3d7c5SThomas E. Spanjaard 	    }
122c1b3d7c5SThomas E. Spanjaard 	}
123c1b3d7c5SThomas E. Spanjaard 	goto begin_continue;
124c1b3d7c5SThomas E. Spanjaard 
125c1b3d7c5SThomas E. Spanjaard     /* ATA DMA data transfer commands */
126c1b3d7c5SThomas E. Spanjaard     case ATA_R_DMA:
127c1b3d7c5SThomas E. Spanjaard 	/* check sanity, setup SG list and DMA engine */
128c1b3d7c5SThomas E. Spanjaard 	if ((error = ch->dma->load(ch->dev, request->data, request->bytecount,
129c1b3d7c5SThomas E. Spanjaard 				   request->flags & ATA_R_READ, ch->dma->sg,
130c1b3d7c5SThomas E. Spanjaard 				   &dummy))) {
131c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev, "setting up DMA failed\n");
132c1b3d7c5SThomas E. Spanjaard 	    request->result = error;
133c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
134c1b3d7c5SThomas E. Spanjaard 	}
135c1b3d7c5SThomas E. Spanjaard 
136c1b3d7c5SThomas E. Spanjaard 	/* issue command */
137c1b3d7c5SThomas E. Spanjaard 	if (ch->hw.command(request)) {
138c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev, "error issuing %s command\n",
139c1b3d7c5SThomas E. Spanjaard 		       ata_cmd2str(request));
140c1b3d7c5SThomas E. Spanjaard 	    request->result = EIO;
141c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
142c1b3d7c5SThomas E. Spanjaard 	}
143c1b3d7c5SThomas E. Spanjaard 
144c1b3d7c5SThomas E. Spanjaard 	/* start DMA engine */
145c1b3d7c5SThomas E. Spanjaard 	if (ch->dma->start && ch->dma->start(request->dev)) {
146c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev, "error starting DMA\n");
147c1b3d7c5SThomas E. Spanjaard 	    request->result = EIO;
148c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
149c1b3d7c5SThomas E. Spanjaard 	}
150c1b3d7c5SThomas E. Spanjaard 	goto begin_continue;
151c1b3d7c5SThomas E. Spanjaard 
152c1b3d7c5SThomas E. Spanjaard     /* ATAPI PIO commands */
153c1b3d7c5SThomas E. Spanjaard     case ATA_R_ATAPI:
154c1b3d7c5SThomas E. Spanjaard 	/* is this just a POLL DSC command ? */
155c1b3d7c5SThomas E. Spanjaard 	if (request->u.atapi.ccb[0] == ATAPI_POLL_DSC) {
1562458a87aSzrj 	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(atadev->unit));
157c1b3d7c5SThomas E. Spanjaard 	    DELAY(10);
158c1b3d7c5SThomas E. Spanjaard 	    if (!(ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_DSC))
159c1b3d7c5SThomas E. Spanjaard 		request->result = EBUSY;
160c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
161c1b3d7c5SThomas E. Spanjaard 	}
162c1b3d7c5SThomas E. Spanjaard 
163c1b3d7c5SThomas E. Spanjaard 	/* start ATAPI operation */
164c1b3d7c5SThomas E. Spanjaard 	if (ch->hw.command(request)) {
165c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev, "error issuing ATA PACKET command\n");
166c1b3d7c5SThomas E. Spanjaard 	    request->result = EIO;
167c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
168c1b3d7c5SThomas E. Spanjaard 	}
169c1b3d7c5SThomas E. Spanjaard 	goto begin_continue;
170c1b3d7c5SThomas E. Spanjaard 
171c1b3d7c5SThomas E. Spanjaard    /* ATAPI DMA commands */
172c1b3d7c5SThomas E. Spanjaard     case ATA_R_ATAPI|ATA_R_DMA:
173c1b3d7c5SThomas E. Spanjaard 	/* is this just a POLL DSC command ? */
174c1b3d7c5SThomas E. Spanjaard 	if (request->u.atapi.ccb[0] == ATAPI_POLL_DSC) {
1752458a87aSzrj 	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(atadev->unit));
176c1b3d7c5SThomas E. Spanjaard 	    DELAY(10);
177c1b3d7c5SThomas E. Spanjaard 	    if (!(ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_DSC))
178c1b3d7c5SThomas E. Spanjaard 		request->result = EBUSY;
179c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
180c1b3d7c5SThomas E. Spanjaard 	}
181c1b3d7c5SThomas E. Spanjaard 
182c1b3d7c5SThomas E. Spanjaard 	/* check sanity, setup SG list and DMA engine */
183c1b3d7c5SThomas E. Spanjaard 	if ((error = ch->dma->load(ch->dev, request->data, request->bytecount,
184c1b3d7c5SThomas E. Spanjaard 				   request->flags & ATA_R_READ, ch->dma->sg,
185c1b3d7c5SThomas E. Spanjaard 				   &dummy))) {
186c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev, "setting up DMA failed\n");
187c1b3d7c5SThomas E. Spanjaard 	    request->result = error;
188c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
189c1b3d7c5SThomas E. Spanjaard 	}
190c1b3d7c5SThomas E. Spanjaard 
191c1b3d7c5SThomas E. Spanjaard 	/* start ATAPI operation */
192c1b3d7c5SThomas E. Spanjaard 	if (ch->hw.command(request)) {
193c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev, "error issuing ATA PACKET command\n");
194c1b3d7c5SThomas E. Spanjaard 	    request->result = EIO;
195c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
196c1b3d7c5SThomas E. Spanjaard 	}
197c1b3d7c5SThomas E. Spanjaard 
198c1b3d7c5SThomas E. Spanjaard 	/* start DMA engine */
199c1b3d7c5SThomas E. Spanjaard 	if (ch->dma->start && ch->dma->start(request->dev)) {
200c1b3d7c5SThomas E. Spanjaard 	    request->result = EIO;
201c1b3d7c5SThomas E. Spanjaard 	    goto begin_finished;
202c1b3d7c5SThomas E. Spanjaard 	}
203c1b3d7c5SThomas E. Spanjaard 	goto begin_continue;
204c1b3d7c5SThomas E. Spanjaard     }
205c1b3d7c5SThomas E. Spanjaard     /* NOT REACHED */
206e3869ec7SSascha Wildner     kprintf("ata_begin_transaction OOPS!!!\n");
207c1b3d7c5SThomas E. Spanjaard 
208c1b3d7c5SThomas E. Spanjaard begin_finished:
209c1b3d7c5SThomas E. Spanjaard     if (ch->dma && ch->dma->flags & ATA_DMA_LOADED)
210c1b3d7c5SThomas E. Spanjaard 	ch->dma->unload(ch->dev);
211c1b3d7c5SThomas E. Spanjaard     return ATA_OP_FINISHED;
212c1b3d7c5SThomas E. Spanjaard 
213c1b3d7c5SThomas E. Spanjaard begin_continue:
214c1b3d7c5SThomas E. Spanjaard     /* caller holds ch->state_mtx */
215c1b3d7c5SThomas E. Spanjaard     callout_reset(&request->callout, request->timeout * hz,
216c1b3d7c5SThomas E. Spanjaard 		  (timeout_t*)ata_timeout, request);
217c1b3d7c5SThomas E. Spanjaard     return ATA_OP_CONTINUES;
218c1b3d7c5SThomas E. Spanjaard }
219c1b3d7c5SThomas E. Spanjaard 
220c1b3d7c5SThomas E. Spanjaard /* must be called with ATA channel locked and state_mtx held */
221c1b3d7c5SThomas E. Spanjaard int
ata_end_transaction(struct ata_request * request)222c1b3d7c5SThomas E. Spanjaard ata_end_transaction(struct ata_request *request)
223c1b3d7c5SThomas E. Spanjaard {
224c04da965Szrj     struct ata_channel *ch = device_get_softc(request->parent);
225c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(request->dev);
226c1b3d7c5SThomas E. Spanjaard     int length;
227c1b3d7c5SThomas E. Spanjaard 
228c1b3d7c5SThomas E. Spanjaard     ATA_DEBUG_RQ(request, "end transaction");
229c1b3d7c5SThomas E. Spanjaard 
230c1b3d7c5SThomas E. Spanjaard     /* clear interrupt and get status */
231c1b3d7c5SThomas E. Spanjaard     request->status = ATA_IDX_INB(ch, ATA_STATUS);
232c1b3d7c5SThomas E. Spanjaard 
233c1b3d7c5SThomas E. Spanjaard     switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_CONTROL)) {
234c1b3d7c5SThomas E. Spanjaard 
235c1b3d7c5SThomas E. Spanjaard     /* ATA PIO data transfer and control commands */
236c1b3d7c5SThomas E. Spanjaard     default:
237c1b3d7c5SThomas E. Spanjaard 
238c1b3d7c5SThomas E. Spanjaard 	/* on timeouts we have no data or anything so just return */
239c1b3d7c5SThomas E. Spanjaard 	if (request->flags & ATA_R_TIMEOUT)
240c1b3d7c5SThomas E. Spanjaard 	    goto end_finished;
241c1b3d7c5SThomas E. Spanjaard 
242c1b3d7c5SThomas E. Spanjaard 	/* on control commands read back registers to the request struct */
243c1b3d7c5SThomas E. Spanjaard 	if (request->flags & ATA_R_CONTROL) {
24437c16061Szrj 	    ch->hw.tf_read(request);
245c1b3d7c5SThomas E. Spanjaard 	}
246c1b3d7c5SThomas E. Spanjaard 
247c1b3d7c5SThomas E. Spanjaard 	/* if we got an error we are done with the HW */
248c1b3d7c5SThomas E. Spanjaard 	if (request->status & ATA_S_ERROR) {
249c1b3d7c5SThomas E. Spanjaard 	    request->error = ATA_IDX_INB(ch, ATA_ERROR);
250c1b3d7c5SThomas E. Spanjaard 	    goto end_finished;
251c1b3d7c5SThomas E. Spanjaard 	}
252c1b3d7c5SThomas E. Spanjaard 
253c1b3d7c5SThomas E. Spanjaard 	/* are we moving data ? */
254c1b3d7c5SThomas E. Spanjaard 	if (request->flags & (ATA_R_READ | ATA_R_WRITE)) {
255c1b3d7c5SThomas E. Spanjaard 
256c1b3d7c5SThomas E. Spanjaard 	    /* if read data get it */
257c1b3d7c5SThomas E. Spanjaard 	    if (request->flags & ATA_R_READ) {
258c1b3d7c5SThomas E. Spanjaard 		int flags = ATA_S_DRQ;
259c1b3d7c5SThomas E. Spanjaard 
260c1b3d7c5SThomas E. Spanjaard 		if (request->u.ata.command != ATA_ATAPI_IDENTIFY)
261c1b3d7c5SThomas E. Spanjaard 		    flags |= ATA_S_READY;
262c1b3d7c5SThomas E. Spanjaard 		if (ata_wait(ch, atadev, flags) < 0) {
263c1b3d7c5SThomas E. Spanjaard 		    device_printf(request->dev,
264c1b3d7c5SThomas E. Spanjaard 				  "timeout waiting for read DRQ\n");
265c1b3d7c5SThomas E. Spanjaard 		    request->result = EIO;
266c1b3d7c5SThomas E. Spanjaard 		    goto end_finished;
267c1b3d7c5SThomas E. Spanjaard 		}
268c1b3d7c5SThomas E. Spanjaard 		ata_pio_read(request, request->transfersize);
269c1b3d7c5SThomas E. Spanjaard 	    }
270c1b3d7c5SThomas E. Spanjaard 
271c1b3d7c5SThomas E. Spanjaard 	    /* update how far we've gotten */
272c1b3d7c5SThomas E. Spanjaard 	    request->donecount += request->transfersize;
273c1b3d7c5SThomas E. Spanjaard 
274c1b3d7c5SThomas E. Spanjaard 	    /* do we need a scoop more ? */
275c1b3d7c5SThomas E. Spanjaard 	    if (request->bytecount > request->donecount) {
276c1b3d7c5SThomas E. Spanjaard 
277c1b3d7c5SThomas E. Spanjaard 		/* set this transfer size according to HW capabilities */
278c1b3d7c5SThomas E. Spanjaard 		request->transfersize =
279c1b3d7c5SThomas E. Spanjaard 		    min((request->bytecount - request->donecount),
280c1b3d7c5SThomas E. Spanjaard 			request->transfersize);
281c1b3d7c5SThomas E. Spanjaard 
282c1b3d7c5SThomas E. Spanjaard 		/* if data write command, output the data */
283c1b3d7c5SThomas E. Spanjaard 		if (request->flags & ATA_R_WRITE) {
284c1b3d7c5SThomas E. Spanjaard 
285c1b3d7c5SThomas E. Spanjaard 		    /* if we get an error here we are done with the HW */
286c1b3d7c5SThomas E. Spanjaard 		    if (ata_wait(ch, atadev, (ATA_S_READY | ATA_S_DRQ)) < 0) {
287c1b3d7c5SThomas E. Spanjaard 			device_printf(request->dev,
288c1b3d7c5SThomas E. Spanjaard 				      "timeout waiting for write DRQ\n");
289c1b3d7c5SThomas E. Spanjaard 			request->status = ATA_IDX_INB(ch, ATA_STATUS);
290c1b3d7c5SThomas E. Spanjaard 			goto end_finished;
291c1b3d7c5SThomas E. Spanjaard 		    }
292c1b3d7c5SThomas E. Spanjaard 
293c1b3d7c5SThomas E. Spanjaard 		    /* output data and return waiting for new interrupt */
294c1b3d7c5SThomas E. Spanjaard 		    ata_pio_write(request, request->transfersize);
295c1b3d7c5SThomas E. Spanjaard 		    goto end_continue;
296c1b3d7c5SThomas E. Spanjaard 		}
297c1b3d7c5SThomas E. Spanjaard 
298c1b3d7c5SThomas E. Spanjaard 		/* if data read command, return & wait for interrupt */
299c1b3d7c5SThomas E. Spanjaard 		if (request->flags & ATA_R_READ)
300c1b3d7c5SThomas E. Spanjaard 		    goto end_continue;
301c1b3d7c5SThomas E. Spanjaard 	    }
302c1b3d7c5SThomas E. Spanjaard 	}
303c1b3d7c5SThomas E. Spanjaard 	/* done with HW */
304c1b3d7c5SThomas E. Spanjaard 	goto end_finished;
305c1b3d7c5SThomas E. Spanjaard 
306c1b3d7c5SThomas E. Spanjaard     /* ATA DMA data transfer commands */
307c1b3d7c5SThomas E. Spanjaard     case ATA_R_DMA:
308c1b3d7c5SThomas E. Spanjaard 
309c1b3d7c5SThomas E. Spanjaard 	/* stop DMA engine and get status */
310c1b3d7c5SThomas E. Spanjaard 	if (ch->dma->stop)
311c1b3d7c5SThomas E. Spanjaard 	    request->dmastat = ch->dma->stop(request->dev);
312c1b3d7c5SThomas E. Spanjaard 
313c1b3d7c5SThomas E. Spanjaard 	/* did we get error or data */
314c1b3d7c5SThomas E. Spanjaard 	if (request->status & ATA_S_ERROR)
315c1b3d7c5SThomas E. Spanjaard 	    request->error = ATA_IDX_INB(ch, ATA_ERROR);
316c1b3d7c5SThomas E. Spanjaard 	else if (request->dmastat & ATA_BMSTAT_ERROR)
317c1b3d7c5SThomas E. Spanjaard 	    request->status |= ATA_S_ERROR;
318c1b3d7c5SThomas E. Spanjaard 	else if (!(request->flags & ATA_R_TIMEOUT))
319c1b3d7c5SThomas E. Spanjaard 	    request->donecount = request->bytecount;
320c1b3d7c5SThomas E. Spanjaard 
321c1b3d7c5SThomas E. Spanjaard 	/* release SG list etc */
322c1b3d7c5SThomas E. Spanjaard 	ch->dma->unload(ch->dev);
323c1b3d7c5SThomas E. Spanjaard 
324c1b3d7c5SThomas E. Spanjaard 	/* done with HW */
325c1b3d7c5SThomas E. Spanjaard 	goto end_finished;
326c1b3d7c5SThomas E. Spanjaard 
327c1b3d7c5SThomas E. Spanjaard     /* ATAPI PIO commands */
328c1b3d7c5SThomas E. Spanjaard     case ATA_R_ATAPI:
329c1b3d7c5SThomas E. Spanjaard 	length = ATA_IDX_INB(ch, ATA_CYL_LSB)|(ATA_IDX_INB(ch, ATA_CYL_MSB)<<8);
330c1b3d7c5SThomas E. Spanjaard 
331c1b3d7c5SThomas E. Spanjaard 	/* on timeouts we have no data or anything so just return */
332c1b3d7c5SThomas E. Spanjaard 	if (request->flags & ATA_R_TIMEOUT)
333c1b3d7c5SThomas E. Spanjaard 	    goto end_finished;
334c1b3d7c5SThomas E. Spanjaard 
335c1b3d7c5SThomas E. Spanjaard 	switch ((ATA_IDX_INB(ch, ATA_IREASON) & (ATA_I_CMD | ATA_I_IN)) |
336c1b3d7c5SThomas E. Spanjaard 		(request->status & ATA_S_DRQ)) {
337c1b3d7c5SThomas E. Spanjaard 
338c1b3d7c5SThomas E. Spanjaard 	case ATAPI_P_CMDOUT:
339c1b3d7c5SThomas E. Spanjaard 	    /* this seems to be needed for some (slow) devices */
340c1b3d7c5SThomas E. Spanjaard 	    DELAY(10);
341c1b3d7c5SThomas E. Spanjaard 
342c1b3d7c5SThomas E. Spanjaard 	    if (!(request->status & ATA_S_DRQ)) {
343c1b3d7c5SThomas E. Spanjaard 		device_printf(request->dev, "command interrupt without DRQ\n");
344c1b3d7c5SThomas E. Spanjaard 		request->status = ATA_S_ERROR;
345c1b3d7c5SThomas E. Spanjaard 		goto end_finished;
346c1b3d7c5SThomas E. Spanjaard 	    }
347c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (int16_t *)request->u.atapi.ccb,
348c1b3d7c5SThomas E. Spanjaard 			       (atadev->param.config &
349c1b3d7c5SThomas E. Spanjaard 				ATA_PROTO_MASK)== ATA_PROTO_ATAPI_12 ? 6 : 8);
350c1b3d7c5SThomas E. Spanjaard 	    /* return wait for interrupt */
351c1b3d7c5SThomas E. Spanjaard 	    goto end_continue;
352c1b3d7c5SThomas E. Spanjaard 
353c1b3d7c5SThomas E. Spanjaard 	case ATAPI_P_WRITE:
354c1b3d7c5SThomas E. Spanjaard 	    if (request->flags & ATA_R_READ) {
355c1b3d7c5SThomas E. Spanjaard 		request->status = ATA_S_ERROR;
356c1b3d7c5SThomas E. Spanjaard 		device_printf(request->dev,
357c1b3d7c5SThomas E. Spanjaard 			      "%s trying to write on read buffer\n",
358c1b3d7c5SThomas E. Spanjaard 			   ata_cmd2str(request));
359c1b3d7c5SThomas E. Spanjaard 		goto end_finished;
360c1b3d7c5SThomas E. Spanjaard 	    }
361c1b3d7c5SThomas E. Spanjaard 	    ata_pio_write(request, length);
362c1b3d7c5SThomas E. Spanjaard 	    request->donecount += length;
363c1b3d7c5SThomas E. Spanjaard 
364c1b3d7c5SThomas E. Spanjaard 	    /* set next transfer size according to HW capabilities */
365c1b3d7c5SThomas E. Spanjaard 	    request->transfersize = min((request->bytecount-request->donecount),
366c1b3d7c5SThomas E. Spanjaard 					request->transfersize);
367c1b3d7c5SThomas E. Spanjaard 	    /* return wait for interrupt */
368c1b3d7c5SThomas E. Spanjaard 	    goto end_continue;
369c1b3d7c5SThomas E. Spanjaard 
370c1b3d7c5SThomas E. Spanjaard 	case ATAPI_P_READ:
371c1b3d7c5SThomas E. Spanjaard 	    if (request->flags & ATA_R_WRITE) {
372c1b3d7c5SThomas E. Spanjaard 		request->status = ATA_S_ERROR;
373c1b3d7c5SThomas E. Spanjaard 		device_printf(request->dev,
374c1b3d7c5SThomas E. Spanjaard 			      "%s trying to read on write buffer\n",
375c1b3d7c5SThomas E. Spanjaard 			   ata_cmd2str(request));
376c1b3d7c5SThomas E. Spanjaard 		goto end_finished;
377c1b3d7c5SThomas E. Spanjaard 	    }
378c1b3d7c5SThomas E. Spanjaard 	    ata_pio_read(request, length);
379c1b3d7c5SThomas E. Spanjaard 	    request->donecount += length;
380c1b3d7c5SThomas E. Spanjaard 
381c1b3d7c5SThomas E. Spanjaard 	    /* set next transfer size according to HW capabilities */
382c1b3d7c5SThomas E. Spanjaard 	    request->transfersize = min((request->bytecount-request->donecount),
383c1b3d7c5SThomas E. Spanjaard 					request->transfersize);
384c1b3d7c5SThomas E. Spanjaard 	    /* return wait for interrupt */
385c1b3d7c5SThomas E. Spanjaard 	    goto end_continue;
386c1b3d7c5SThomas E. Spanjaard 
387c1b3d7c5SThomas E. Spanjaard 	case ATAPI_P_DONEDRQ:
388c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev,
389c1b3d7c5SThomas E. Spanjaard 			  "WARNING - %s DONEDRQ non conformant device\n",
390c1b3d7c5SThomas E. Spanjaard 			  ata_cmd2str(request));
391c1b3d7c5SThomas E. Spanjaard 	    if (request->flags & ATA_R_READ) {
392c1b3d7c5SThomas E. Spanjaard 		ata_pio_read(request, length);
393c1b3d7c5SThomas E. Spanjaard 		request->donecount += length;
394c1b3d7c5SThomas E. Spanjaard 	    }
395c1b3d7c5SThomas E. Spanjaard 	    else if (request->flags & ATA_R_WRITE) {
396c1b3d7c5SThomas E. Spanjaard 		ata_pio_write(request, length);
397c1b3d7c5SThomas E. Spanjaard 		request->donecount += length;
398c1b3d7c5SThomas E. Spanjaard 	    }
399c1b3d7c5SThomas E. Spanjaard 	    else
400c1b3d7c5SThomas E. Spanjaard 		request->status = ATA_S_ERROR;
401c1b3d7c5SThomas E. Spanjaard 	    /* FALLTHROUGH */
402c1b3d7c5SThomas E. Spanjaard 
403c1b3d7c5SThomas E. Spanjaard 	case ATAPI_P_ABORT:
404c1b3d7c5SThomas E. Spanjaard 	case ATAPI_P_DONE:
405c1b3d7c5SThomas E. Spanjaard 	    if (request->status & (ATA_S_ERROR | ATA_S_DWF))
406c1b3d7c5SThomas E. Spanjaard 		request->error = ATA_IDX_INB(ch, ATA_ERROR);
407c1b3d7c5SThomas E. Spanjaard 	    goto end_finished;
408c1b3d7c5SThomas E. Spanjaard 
409c1b3d7c5SThomas E. Spanjaard 	default:
410c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev, "unknown transfer phase\n");
411c1b3d7c5SThomas E. Spanjaard 	    request->status = ATA_S_ERROR;
412c1b3d7c5SThomas E. Spanjaard 	}
413c1b3d7c5SThomas E. Spanjaard 
414c1b3d7c5SThomas E. Spanjaard 	/* done with HW */
415c1b3d7c5SThomas E. Spanjaard 	goto end_finished;
416c1b3d7c5SThomas E. Spanjaard 
417c1b3d7c5SThomas E. Spanjaard     /* ATAPI DMA commands */
418c1b3d7c5SThomas E. Spanjaard     case ATA_R_ATAPI|ATA_R_DMA:
419c1b3d7c5SThomas E. Spanjaard 
420c1b3d7c5SThomas E. Spanjaard 	/* stop DMA engine and get status */
421c1b3d7c5SThomas E. Spanjaard 	if (ch->dma->stop)
422c1b3d7c5SThomas E. Spanjaard 	    request->dmastat = ch->dma->stop(request->dev);
423c1b3d7c5SThomas E. Spanjaard 
424c1b3d7c5SThomas E. Spanjaard 	/* did we get error or data */
425c1b3d7c5SThomas E. Spanjaard 	if (request->status & (ATA_S_ERROR | ATA_S_DWF))
426c1b3d7c5SThomas E. Spanjaard 	    request->error = ATA_IDX_INB(ch, ATA_ERROR);
427c1b3d7c5SThomas E. Spanjaard 	else if (request->dmastat & ATA_BMSTAT_ERROR)
428c1b3d7c5SThomas E. Spanjaard 	    request->status |= ATA_S_ERROR;
429c1b3d7c5SThomas E. Spanjaard 	else if (!(request->flags & ATA_R_TIMEOUT))
430c1b3d7c5SThomas E. Spanjaard 	    request->donecount = request->bytecount;
431c1b3d7c5SThomas E. Spanjaard 
432c1b3d7c5SThomas E. Spanjaard 	/* release SG list etc */
433c1b3d7c5SThomas E. Spanjaard 	ch->dma->unload(ch->dev);
434c1b3d7c5SThomas E. Spanjaard 
435c1b3d7c5SThomas E. Spanjaard 	/* done with HW */
436c1b3d7c5SThomas E. Spanjaard 	goto end_finished;
437c1b3d7c5SThomas E. Spanjaard     }
438c1b3d7c5SThomas E. Spanjaard     /* NOT REACHED */
439e3869ec7SSascha Wildner     kprintf("ata_end_transaction OOPS!!\n");
440c1b3d7c5SThomas E. Spanjaard 
441c1b3d7c5SThomas E. Spanjaard end_finished:
442*eb67213aSMatthew Dillon     callout_cancel(&request->callout);
443c1b3d7c5SThomas E. Spanjaard     return ATA_OP_FINISHED;
444c1b3d7c5SThomas E. Spanjaard 
445c1b3d7c5SThomas E. Spanjaard end_continue:
446c1b3d7c5SThomas E. Spanjaard     return ATA_OP_CONTINUES;
447c1b3d7c5SThomas E. Spanjaard }
448c1b3d7c5SThomas E. Spanjaard 
449c1b3d7c5SThomas E. Spanjaard /* must be called with ATA channel locked and state_mtx held */
450c1b3d7c5SThomas E. Spanjaard void
ata_generic_reset(device_t dev)451c1b3d7c5SThomas E. Spanjaard ata_generic_reset(device_t dev)
452c1b3d7c5SThomas E. Spanjaard {
453c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(dev);
454c1b3d7c5SThomas E. Spanjaard 
455c1b3d7c5SThomas E. Spanjaard     u_int8_t ostat0 = 0, stat0 = 0, ostat1 = 0, stat1 = 0;
456c1b3d7c5SThomas E. Spanjaard     u_int8_t err = 0, lsb = 0, msb = 0;
457c1b3d7c5SThomas E. Spanjaard     int mask = 0, timeout;
458c1b3d7c5SThomas E. Spanjaard 
459c1b3d7c5SThomas E. Spanjaard     /* do we have any signs of ATA/ATAPI HW being present ? */
4602458a87aSzrj     ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(ATA_MASTER));
461c1b3d7c5SThomas E. Spanjaard     DELAY(10);
462c1b3d7c5SThomas E. Spanjaard     ostat0 = ATA_IDX_INB(ch, ATA_STATUS);
463c1b3d7c5SThomas E. Spanjaard     if ((ostat0 & 0xf8) != 0xf8 && ostat0 != 0xa5) {
464c1b3d7c5SThomas E. Spanjaard 	stat0 = ATA_S_BUSY;
465c1b3d7c5SThomas E. Spanjaard 	mask |= 0x01;
466c1b3d7c5SThomas E. Spanjaard     }
467c1b3d7c5SThomas E. Spanjaard 
468c1b3d7c5SThomas E. Spanjaard     /* in some setups we dont want to test for a slave */
469c1b3d7c5SThomas E. Spanjaard     if (!(ch->flags & ATA_NO_SLAVE)) {
4702458a87aSzrj 	ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(ATA_SLAVE));
471c1b3d7c5SThomas E. Spanjaard 	DELAY(10);
472c1b3d7c5SThomas E. Spanjaard 	ostat1 = ATA_IDX_INB(ch, ATA_STATUS);
473c1b3d7c5SThomas E. Spanjaard 	if ((ostat1 & 0xf8) != 0xf8 && ostat1 != 0xa5) {
474c1b3d7c5SThomas E. Spanjaard 	    stat1 = ATA_S_BUSY;
475c1b3d7c5SThomas E. Spanjaard 	    mask |= 0x02;
476c1b3d7c5SThomas E. Spanjaard 	}
477c1b3d7c5SThomas E. Spanjaard     }
478c1b3d7c5SThomas E. Spanjaard 
479c1b3d7c5SThomas E. Spanjaard     if (bootverbose)
480c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "reset tp1 mask=%02x ostat0=%02x ostat1=%02x\n",
481c1b3d7c5SThomas E. Spanjaard 		      mask, ostat0, ostat1);
482c1b3d7c5SThomas E. Spanjaard 
483c1b3d7c5SThomas E. Spanjaard     /* if nothing showed up there is no need to get any further */
484c1b3d7c5SThomas E. Spanjaard     /* XXX SOS is that too strong?, we just might loose devices here */
485c1b3d7c5SThomas E. Spanjaard     ch->devices = 0;
486c1b3d7c5SThomas E. Spanjaard     if (!mask)
487c1b3d7c5SThomas E. Spanjaard 	return;
488c1b3d7c5SThomas E. Spanjaard 
489c1b3d7c5SThomas E. Spanjaard     /* reset (both) devices on this channel */
4902458a87aSzrj     ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(ATA_MASTER));
491c1b3d7c5SThomas E. Spanjaard     DELAY(10);
492c1b3d7c5SThomas E. Spanjaard     ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_IDS | ATA_A_RESET);
493c1b3d7c5SThomas E. Spanjaard     ata_udelay(10000);
494c1b3d7c5SThomas E. Spanjaard     ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_IDS);
495c1b3d7c5SThomas E. Spanjaard     ata_udelay(100000);
496c1b3d7c5SThomas E. Spanjaard     ATA_IDX_INB(ch, ATA_ERROR);
497c1b3d7c5SThomas E. Spanjaard 
498c1b3d7c5SThomas E. Spanjaard     /* wait for BUSY to go inactive */
499c1b3d7c5SThomas E. Spanjaard     for (timeout = 0; timeout < 310; timeout++) {
500c1b3d7c5SThomas E. Spanjaard 	if ((mask & 0x01) && (stat0 & ATA_S_BUSY)) {
5012458a87aSzrj 	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(ATA_MASTER));
502c1b3d7c5SThomas E. Spanjaard 	    DELAY(10);
503c1b3d7c5SThomas E. Spanjaard 	    err = ATA_IDX_INB(ch, ATA_ERROR);
504c1b3d7c5SThomas E. Spanjaard 	    lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
505c1b3d7c5SThomas E. Spanjaard 	    msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
506c1b3d7c5SThomas E. Spanjaard 	    stat0 = ATA_IDX_INB(ch, ATA_STATUS);
507c1b3d7c5SThomas E. Spanjaard 	    if (bootverbose)
508c1b3d7c5SThomas E. Spanjaard 		device_printf(dev,
509c1b3d7c5SThomas E. Spanjaard 			      "stat0=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
510c1b3d7c5SThomas E. Spanjaard 			      stat0, err, lsb, msb);
511c1b3d7c5SThomas E. Spanjaard 	    if (stat0 == err && lsb == err && msb == err &&
512c1b3d7c5SThomas E. Spanjaard 		timeout > (stat0 & ATA_S_BUSY ? 100 : 10))
513c1b3d7c5SThomas E. Spanjaard 		mask &= ~0x01;
514c1b3d7c5SThomas E. Spanjaard 	    if (!(stat0 & ATA_S_BUSY)) {
515c1b3d7c5SThomas E. Spanjaard 		if ((err & 0x7f) == ATA_E_ILI) {
516c1b3d7c5SThomas E. Spanjaard 		    if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) {
517c1b3d7c5SThomas E. Spanjaard 			ch->devices |= ATA_ATAPI_MASTER;
518c1b3d7c5SThomas E. Spanjaard 		    }
519c1b3d7c5SThomas E. Spanjaard 		    else if (stat0 & ATA_S_READY) {
520c1b3d7c5SThomas E. Spanjaard 			ch->devices |= ATA_ATA_MASTER;
521c1b3d7c5SThomas E. Spanjaard 		    }
522c1b3d7c5SThomas E. Spanjaard 		}
523c1b3d7c5SThomas E. Spanjaard 		else if ((stat0 & 0x0f) && err == lsb && err == msb) {
524c1b3d7c5SThomas E. Spanjaard 		    stat0 |= ATA_S_BUSY;
525c1b3d7c5SThomas E. Spanjaard 		}
526c1b3d7c5SThomas E. Spanjaard 	    }
527c1b3d7c5SThomas E. Spanjaard 	}
528c1b3d7c5SThomas E. Spanjaard 
529c1b3d7c5SThomas E. Spanjaard 	if ((mask & 0x02) && (stat1 & ATA_S_BUSY) &&
530c1b3d7c5SThomas E. Spanjaard 	    !((mask & 0x01) && (stat0 & ATA_S_BUSY))) {
5312458a87aSzrj 	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(ATA_SLAVE));
532c1b3d7c5SThomas E. Spanjaard 	    DELAY(10);
533c1b3d7c5SThomas E. Spanjaard 	    err = ATA_IDX_INB(ch, ATA_ERROR);
534c1b3d7c5SThomas E. Spanjaard 	    lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
535c1b3d7c5SThomas E. Spanjaard 	    msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
536c1b3d7c5SThomas E. Spanjaard 	    stat1 = ATA_IDX_INB(ch, ATA_STATUS);
537c1b3d7c5SThomas E. Spanjaard 	    if (bootverbose)
538c1b3d7c5SThomas E. Spanjaard 		device_printf(dev,
539c1b3d7c5SThomas E. Spanjaard 			      "stat1=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
540c1b3d7c5SThomas E. Spanjaard 			      stat1, err, lsb, msb);
541c1b3d7c5SThomas E. Spanjaard 	    if (stat1 == err && lsb == err && msb == err &&
542c1b3d7c5SThomas E. Spanjaard 		timeout > (stat1 & ATA_S_BUSY ? 100 : 10))
543c1b3d7c5SThomas E. Spanjaard 		mask &= ~0x02;
544c1b3d7c5SThomas E. Spanjaard 	    if (!(stat1 & ATA_S_BUSY)) {
545c1b3d7c5SThomas E. Spanjaard 		if ((err & 0x7f) == ATA_E_ILI) {
546c1b3d7c5SThomas E. Spanjaard 		    if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) {
547c1b3d7c5SThomas E. Spanjaard 			ch->devices |= ATA_ATAPI_SLAVE;
548c1b3d7c5SThomas E. Spanjaard 		    }
549c1b3d7c5SThomas E. Spanjaard 		    else if (stat1 & ATA_S_READY) {
550c1b3d7c5SThomas E. Spanjaard 			ch->devices |= ATA_ATA_SLAVE;
551c1b3d7c5SThomas E. Spanjaard 		    }
552c1b3d7c5SThomas E. Spanjaard 		}
553c1b3d7c5SThomas E. Spanjaard 		else if ((stat1 & 0x0f) && err == lsb && err == msb) {
554c1b3d7c5SThomas E. Spanjaard 		    stat1 |= ATA_S_BUSY;
555c1b3d7c5SThomas E. Spanjaard 		}
556c1b3d7c5SThomas E. Spanjaard 	    }
557c1b3d7c5SThomas E. Spanjaard 	}
558c1b3d7c5SThomas E. Spanjaard 
559c1b3d7c5SThomas E. Spanjaard 	if (mask == 0x00)       /* nothing to wait for */
560c1b3d7c5SThomas E. Spanjaard 	    break;
561c1b3d7c5SThomas E. Spanjaard 	if (mask == 0x01)       /* wait for master only */
562c1b3d7c5SThomas E. Spanjaard 	    if (!(stat0 & ATA_S_BUSY) || (stat0 == 0xff && timeout > 10))
563c1b3d7c5SThomas E. Spanjaard 		break;
564c1b3d7c5SThomas E. Spanjaard 	if (mask == 0x02)       /* wait for slave only */
565c1b3d7c5SThomas E. Spanjaard 	    if (!(stat1 & ATA_S_BUSY) || (stat1 == 0xff && timeout > 10))
566c1b3d7c5SThomas E. Spanjaard 		break;
567c1b3d7c5SThomas E. Spanjaard 	if (mask == 0x03) {     /* wait for both master & slave */
568c1b3d7c5SThomas E. Spanjaard 	    if (!(stat0 & ATA_S_BUSY) && !(stat1 & ATA_S_BUSY))
569c1b3d7c5SThomas E. Spanjaard 		break;
570c1b3d7c5SThomas E. Spanjaard 	    if ((stat0 == 0xff) && (timeout > 20))
571c1b3d7c5SThomas E. Spanjaard 		mask &= ~0x01;
572c1b3d7c5SThomas E. Spanjaard 	    if ((stat1 == 0xff) && (timeout > 20))
573c1b3d7c5SThomas E. Spanjaard 		mask &= ~0x02;
574c1b3d7c5SThomas E. Spanjaard 	}
575c1b3d7c5SThomas E. Spanjaard 	ata_udelay(100000);
576c1b3d7c5SThomas E. Spanjaard     }
577c1b3d7c5SThomas E. Spanjaard 
578c1b3d7c5SThomas E. Spanjaard     if (bootverbose)
57937c16061Szrj 	device_printf(dev, "reset tp2 stat0=%02x stat1=%02x devices=0x%x\n",
58037c16061Szrj 		      stat0, stat1, ch->devices);
581c1b3d7c5SThomas E. Spanjaard }
582c1b3d7c5SThomas E. Spanjaard 
583c1b3d7c5SThomas E. Spanjaard /* must be called with ATA channel locked and state_mtx held */
5848406cf70SSascha Wildner static int
ata_generic_status(device_t dev)585c1b3d7c5SThomas E. Spanjaard ata_generic_status(device_t dev)
586c1b3d7c5SThomas E. Spanjaard {
587c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(dev);
588c1b3d7c5SThomas E. Spanjaard 
589c1b3d7c5SThomas E. Spanjaard     if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
590c1b3d7c5SThomas E. Spanjaard 	DELAY(100);
591c1b3d7c5SThomas E. Spanjaard 	if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
592c1b3d7c5SThomas E. Spanjaard 	    return 0;
593c1b3d7c5SThomas E. Spanjaard     }
594c1b3d7c5SThomas E. Spanjaard     return 1;
595c1b3d7c5SThomas E. Spanjaard }
596c1b3d7c5SThomas E. Spanjaard 
597c1b3d7c5SThomas E. Spanjaard static int
ata_wait(struct ata_channel * ch,struct ata_device * atadev,u_int8_t mask)598c1b3d7c5SThomas E. Spanjaard ata_wait(struct ata_channel *ch, struct ata_device *atadev, u_int8_t mask)
599c1b3d7c5SThomas E. Spanjaard {
600c1b3d7c5SThomas E. Spanjaard     u_int8_t status;
601c1b3d7c5SThomas E. Spanjaard     int timeout = 0;
602c1b3d7c5SThomas E. Spanjaard 
603c1b3d7c5SThomas E. Spanjaard     DELAY(1);
604c1b3d7c5SThomas E. Spanjaard 
605c1b3d7c5SThomas E. Spanjaard     /* wait at max 1 second for device to get !BUSY */
606c1b3d7c5SThomas E. Spanjaard     while (timeout < 1000000) {
607c1b3d7c5SThomas E. Spanjaard 	status = ATA_IDX_INB(ch, ATA_ALTSTAT);
608c1b3d7c5SThomas E. Spanjaard 
609c1b3d7c5SThomas E. Spanjaard 	/* if drive fails status, reselect the drive and try again */
610c1b3d7c5SThomas E. Spanjaard 	if (status == 0xff) {
6112458a87aSzrj 	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(atadev->unit));
612c1b3d7c5SThomas E. Spanjaard 	    timeout += 1000;
613c1b3d7c5SThomas E. Spanjaard 	    DELAY(1000);
614c1b3d7c5SThomas E. Spanjaard 	    continue;
615c1b3d7c5SThomas E. Spanjaard 	}
616c1b3d7c5SThomas E. Spanjaard 
617c1b3d7c5SThomas E. Spanjaard 	/* are we done ? */
618c1b3d7c5SThomas E. Spanjaard 	if (!(status & ATA_S_BUSY))
619c1b3d7c5SThomas E. Spanjaard 	    break;
620c1b3d7c5SThomas E. Spanjaard 
621c1b3d7c5SThomas E. Spanjaard 	if (timeout > 1000) {
622c1b3d7c5SThomas E. Spanjaard 	    timeout += 1000;
623c1b3d7c5SThomas E. Spanjaard 	    DELAY(1000);
624c1b3d7c5SThomas E. Spanjaard 	}
625c1b3d7c5SThomas E. Spanjaard 	else {
626c1b3d7c5SThomas E. Spanjaard 	    timeout += 10;
627c1b3d7c5SThomas E. Spanjaard 	    DELAY(10);
628c1b3d7c5SThomas E. Spanjaard 	}
629c1b3d7c5SThomas E. Spanjaard     }
630c1b3d7c5SThomas E. Spanjaard     if (timeout >= 1000000)
631c1b3d7c5SThomas E. Spanjaard 	return -2;
632c1b3d7c5SThomas E. Spanjaard     if (!mask)
633c1b3d7c5SThomas E. Spanjaard 	return (status & ATA_S_ERROR);
634c1b3d7c5SThomas E. Spanjaard 
635c1b3d7c5SThomas E. Spanjaard     DELAY(1);
636c1b3d7c5SThomas E. Spanjaard 
637c1b3d7c5SThomas E. Spanjaard     /* wait 50 msec for bits wanted */
638c1b3d7c5SThomas E. Spanjaard     timeout = 5000;
639c1b3d7c5SThomas E. Spanjaard     while (timeout--) {
640c1b3d7c5SThomas E. Spanjaard 	status = ATA_IDX_INB(ch, ATA_ALTSTAT);
641c1b3d7c5SThomas E. Spanjaard 	if ((status & mask) == mask)
642c1b3d7c5SThomas E. Spanjaard 	    return (status & ATA_S_ERROR);
643c1b3d7c5SThomas E. Spanjaard 	DELAY(10);
644c1b3d7c5SThomas E. Spanjaard     }
645c1b3d7c5SThomas E. Spanjaard     return -3;
646c1b3d7c5SThomas E. Spanjaard }
647c1b3d7c5SThomas E. Spanjaard 
648c1b3d7c5SThomas E. Spanjaard int
ata_generic_command(struct ata_request * request)649c1b3d7c5SThomas E. Spanjaard ata_generic_command(struct ata_request *request)
650c1b3d7c5SThomas E. Spanjaard {
651c04da965Szrj     struct ata_channel *ch = device_get_softc(request->parent);
652c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(request->dev);
653c1b3d7c5SThomas E. Spanjaard 
654c1b3d7c5SThomas E. Spanjaard     /* select device */
6552458a87aSzrj     ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(atadev->unit));
656c1b3d7c5SThomas E. Spanjaard 
657c1b3d7c5SThomas E. Spanjaard     /* ready to issue command ? */
658c1b3d7c5SThomas E. Spanjaard     if (ata_wait(ch, atadev, 0) < 0) {
659c1b3d7c5SThomas E. Spanjaard 	device_printf(request->dev, "timeout waiting to issue command\n");
660c1b3d7c5SThomas E. Spanjaard 	return -1;
661c1b3d7c5SThomas E. Spanjaard     }
662c1b3d7c5SThomas E. Spanjaard 
663c1b3d7c5SThomas E. Spanjaard     /* enable interrupt */
664c1b3d7c5SThomas E. Spanjaard     ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT);
665c1b3d7c5SThomas E. Spanjaard 
666c1b3d7c5SThomas E. Spanjaard     if (request->flags & ATA_R_ATAPI) {
667c1b3d7c5SThomas E. Spanjaard 	int timeout = 5000;
668c1b3d7c5SThomas E. Spanjaard 
669c1b3d7c5SThomas E. Spanjaard 	/* issue packet command to controller */
670c1b3d7c5SThomas E. Spanjaard 	if (request->flags & ATA_R_DMA) {
671c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_FEATURE, ATA_F_DMA);
672c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0);
673c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_CYL_MSB, 0);
674c1b3d7c5SThomas E. Spanjaard 	}
675c1b3d7c5SThomas E. Spanjaard 	else {
676c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_FEATURE, 0);
677c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->transfersize);
678c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->transfersize >> 8);
679c1b3d7c5SThomas E. Spanjaard 	}
680c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_COMMAND, ATA_PACKET_CMD);
681c1b3d7c5SThomas E. Spanjaard 
682c1b3d7c5SThomas E. Spanjaard 	/* command interrupt device ? just return and wait for interrupt */
683c1b3d7c5SThomas E. Spanjaard 	if ((atadev->param.config & ATA_DRQ_MASK) == ATA_DRQ_INTR)
684c1b3d7c5SThomas E. Spanjaard 	    return 0;
685c1b3d7c5SThomas E. Spanjaard 
686c1b3d7c5SThomas E. Spanjaard 	/* wait for ready to write ATAPI command block */
687c1b3d7c5SThomas E. Spanjaard 	while (timeout--) {
688c1b3d7c5SThomas E. Spanjaard 	    int reason = ATA_IDX_INB(ch, ATA_IREASON);
689c1b3d7c5SThomas E. Spanjaard 	    int status = ATA_IDX_INB(ch, ATA_STATUS);
690c1b3d7c5SThomas E. Spanjaard 
691c1b3d7c5SThomas E. Spanjaard 	    if (((reason & (ATA_I_CMD | ATA_I_IN)) |
692c1b3d7c5SThomas E. Spanjaard 		 (status & (ATA_S_DRQ | ATA_S_BUSY))) == ATAPI_P_CMDOUT)
693c1b3d7c5SThomas E. Spanjaard 		break;
694c1b3d7c5SThomas E. Spanjaard 	    DELAY(20);
695c1b3d7c5SThomas E. Spanjaard 	}
696c1b3d7c5SThomas E. Spanjaard 	if (timeout <= 0) {
697c1b3d7c5SThomas E. Spanjaard 	    device_printf(request->dev, "timeout waiting for ATAPI ready\n");
698c1b3d7c5SThomas E. Spanjaard 	    request->result = EIO;
699c1b3d7c5SThomas E. Spanjaard 	    return -1;
700c1b3d7c5SThomas E. Spanjaard 	}
701c1b3d7c5SThomas E. Spanjaard 
702c1b3d7c5SThomas E. Spanjaard 	/* this seems to be needed for some (slow) devices */
703c1b3d7c5SThomas E. Spanjaard 	DELAY(10);
704c1b3d7c5SThomas E. Spanjaard 
705c1b3d7c5SThomas E. Spanjaard 	/* output command block */
706c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (int16_t *)request->u.atapi.ccb,
707c1b3d7c5SThomas E. Spanjaard 			   (atadev->param.config & ATA_PROTO_MASK) ==
708c1b3d7c5SThomas E. Spanjaard 			   ATA_PROTO_ATAPI_12 ? 6 : 8);
709c1b3d7c5SThomas E. Spanjaard     }
710c1b3d7c5SThomas E. Spanjaard     else {
71137c16061Szrj 	ch->hw.tf_write(request);
712c04da965Szrj 
713c04da965Szrj 	/* issue command to controller */
714c04da965Szrj 	ATA_IDX_OUTB(ch, ATA_COMMAND, request->u.ata.command);
715c04da965Szrj     }
716c04da965Szrj     return 0;
717c04da965Szrj }
718c04da965Szrj 
719c04da965Szrj static void
ata_tf_read(struct ata_request * request)720c04da965Szrj ata_tf_read(struct ata_request *request)
721c04da965Szrj {
722c04da965Szrj     struct ata_channel *ch = device_get_softc(request->parent);
723c04da965Szrj     struct ata_device *atadev = device_get_softc(request->dev);
724c04da965Szrj 
725c04da965Szrj     if (atadev->flags & ATA_D_48BIT_ACTIVE) {
726c04da965Szrj 	ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT | ATA_A_HOB);
727c04da965Szrj 	request->u.ata.count = (ATA_IDX_INB(ch, ATA_COUNT) << 8);
728c04da965Szrj 	request->u.ata.lba =
729c04da965Szrj 	    ((u_int64_t)(ATA_IDX_INB(ch, ATA_SECTOR)) << 24) |
730c04da965Szrj 	    ((u_int64_t)(ATA_IDX_INB(ch, ATA_CYL_LSB)) << 32) |
731c04da965Szrj 	    ((u_int64_t)(ATA_IDX_INB(ch, ATA_CYL_MSB)) << 40);
732c04da965Szrj 
733c04da965Szrj 	ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT);
734c04da965Szrj 	request->u.ata.count |= ATA_IDX_INB(ch, ATA_COUNT);
735c04da965Szrj 	request->u.ata.lba |=
736c04da965Szrj 	    (ATA_IDX_INB(ch, ATA_SECTOR) |
737c04da965Szrj 	     (ATA_IDX_INB(ch, ATA_CYL_LSB) << 8) |
738c04da965Szrj 	     (ATA_IDX_INB(ch, ATA_CYL_MSB) << 16));
739c04da965Szrj     }
740c04da965Szrj     else {
741c04da965Szrj 	request->u.ata.count = ATA_IDX_INB(ch, ATA_COUNT);
742c04da965Szrj 	request->u.ata.lba = ATA_IDX_INB(ch, ATA_SECTOR) |
743c04da965Szrj 			     (ATA_IDX_INB(ch, ATA_CYL_LSB) << 8) |
744c04da965Szrj 			     (ATA_IDX_INB(ch, ATA_CYL_MSB) << 16) |
745c04da965Szrj 			     ((ATA_IDX_INB(ch, ATA_DRIVE) & 0xf) << 24);
746c04da965Szrj     }
747c04da965Szrj }
748c04da965Szrj 
749c04da965Szrj static void
ata_tf_write(struct ata_request * request)750c04da965Szrj ata_tf_write(struct ata_request *request)
751c04da965Szrj {
752c04da965Szrj     struct ata_channel *ch = device_get_softc(request->parent);
753c04da965Szrj     struct ata_device *atadev = device_get_softc(request->dev);
754c04da965Szrj 
755c1b3d7c5SThomas E. Spanjaard     if (atadev->flags & ATA_D_48BIT_ACTIVE) {
756c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature >> 8);
757c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature);
758c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count >> 8);
759c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count);
760c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba >> 24);
761c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba);
762c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 32);
763c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 8);
764c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 40);
765c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 16);
7662458a87aSzrj 	ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_LBA | ATA_DEV(atadev->unit));
767c1b3d7c5SThomas E. Spanjaard     }
768c1b3d7c5SThomas E. Spanjaard     else {
769c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature);
770c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count);
771c1b3d7c5SThomas E. Spanjaard 	if (atadev->flags & ATA_D_USE_CHS) {
772c1b3d7c5SThomas E. Spanjaard 	    int heads, sectors;
773c1b3d7c5SThomas E. Spanjaard 
774c1b3d7c5SThomas E. Spanjaard 	    if (atadev->param.atavalid & ATA_FLAG_54_58) {
775c1b3d7c5SThomas E. Spanjaard 		heads = atadev->param.current_heads;
776c1b3d7c5SThomas E. Spanjaard 		sectors = atadev->param.current_sectors;
777c1b3d7c5SThomas E. Spanjaard 	    }
778c1b3d7c5SThomas E. Spanjaard 	    else {
779c1b3d7c5SThomas E. Spanjaard 		heads = atadev->param.heads;
780c1b3d7c5SThomas E. Spanjaard 		sectors = atadev->param.sectors;
781c1b3d7c5SThomas E. Spanjaard 	    }
782c04da965Szrj 
783c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_SECTOR, (request->u.ata.lba % sectors)+1);
784c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_CYL_LSB,
785c1b3d7c5SThomas E. Spanjaard 			 (request->u.ata.lba / (sectors * heads)));
786c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_CYL_MSB,
787c1b3d7c5SThomas E. Spanjaard 			 (request->u.ata.lba / (sectors * heads)) >> 8);
7882458a87aSzrj 	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(atadev->unit) |
789c1b3d7c5SThomas E. Spanjaard 			 (((request->u.ata.lba% (sectors * heads)) /
790c1b3d7c5SThomas E. Spanjaard 			   sectors) & 0xf));
791c1b3d7c5SThomas E. Spanjaard 	}
792c1b3d7c5SThomas E. Spanjaard 	else {
793c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba);
794c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 8);
795c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 16);
796c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTB(ch, ATA_DRIVE,
7972458a87aSzrj 			 ATA_D_IBM | ATA_D_LBA | ATA_DEV(atadev->unit) |
798c1b3d7c5SThomas E. Spanjaard 			 ((request->u.ata.lba >> 24) & 0x0f));
799c1b3d7c5SThomas E. Spanjaard 	}
800c1b3d7c5SThomas E. Spanjaard     }
801c1b3d7c5SThomas E. Spanjaard }
802c1b3d7c5SThomas E. Spanjaard 
803c1b3d7c5SThomas E. Spanjaard static void
ata_pio_read(struct ata_request * request,int length)804c1b3d7c5SThomas E. Spanjaard ata_pio_read(struct ata_request *request, int length)
805c1b3d7c5SThomas E. Spanjaard {
806c04da965Szrj     struct ata_channel *ch = device_get_softc(request->parent);
807c1b3d7c5SThomas E. Spanjaard     int size = min(request->transfersize, length);
808c1b3d7c5SThomas E. Spanjaard     int resid;
809c1b3d7c5SThomas E. Spanjaard 
810c1b3d7c5SThomas E. Spanjaard     if (ch->flags & ATA_USE_16BIT || (size % sizeof(int32_t)))
811c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_INSW_STRM(ch, ATA_DATA,
812c1b3d7c5SThomas E. Spanjaard 			  (void*)((uintptr_t)request->data+request->donecount),
813c1b3d7c5SThomas E. Spanjaard 			  size / sizeof(int16_t));
814c1b3d7c5SThomas E. Spanjaard     else
815c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_INSL_STRM(ch, ATA_DATA,
816c1b3d7c5SThomas E. Spanjaard 			  (void*)((uintptr_t)request->data+request->donecount),
817c1b3d7c5SThomas E. Spanjaard 			  size / sizeof(int32_t));
818c1b3d7c5SThomas E. Spanjaard 
819c1b3d7c5SThomas E. Spanjaard     if (request->transfersize < length) {
820c1b3d7c5SThomas E. Spanjaard 	device_printf(request->dev, "WARNING - %s read data overrun %d>%d\n",
821c1b3d7c5SThomas E. Spanjaard 		   ata_cmd2str(request), length, request->transfersize);
822c1b3d7c5SThomas E. Spanjaard 	for (resid = request->transfersize; resid < length;
823c1b3d7c5SThomas E. Spanjaard 	     resid += sizeof(int16_t))
824c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_INW(ch, ATA_DATA);
825c1b3d7c5SThomas E. Spanjaard     }
826c1b3d7c5SThomas E. Spanjaard }
827c1b3d7c5SThomas E. Spanjaard 
828c1b3d7c5SThomas E. Spanjaard static void
ata_pio_write(struct ata_request * request,int length)829c1b3d7c5SThomas E. Spanjaard ata_pio_write(struct ata_request *request, int length)
830c1b3d7c5SThomas E. Spanjaard {
831c04da965Szrj     struct ata_channel *ch = device_get_softc(request->parent);
832c1b3d7c5SThomas E. Spanjaard     int size = min(request->transfersize, length);
833c1b3d7c5SThomas E. Spanjaard     int resid;
834c1b3d7c5SThomas E. Spanjaard 
835c1b3d7c5SThomas E. Spanjaard     if (ch->flags & ATA_USE_16BIT || (size % sizeof(int32_t)))
836c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTSW_STRM(ch, ATA_DATA,
837c1b3d7c5SThomas E. Spanjaard 			   (void*)((uintptr_t)request->data+request->donecount),
838c1b3d7c5SThomas E. Spanjaard 			   size / sizeof(int16_t));
839c1b3d7c5SThomas E. Spanjaard     else
840c1b3d7c5SThomas E. Spanjaard 	ATA_IDX_OUTSL_STRM(ch, ATA_DATA,
841c1b3d7c5SThomas E. Spanjaard 			   (void*)((uintptr_t)request->data+request->donecount),
842c1b3d7c5SThomas E. Spanjaard 			   size / sizeof(int32_t));
843c1b3d7c5SThomas E. Spanjaard 
844c1b3d7c5SThomas E. Spanjaard     if (request->transfersize < length) {
845c1b3d7c5SThomas E. Spanjaard 	device_printf(request->dev, "WARNING - %s write data underrun %d>%d\n",
846c1b3d7c5SThomas E. Spanjaard 		   ata_cmd2str(request), length, request->transfersize);
847c1b3d7c5SThomas E. Spanjaard 	for (resid = request->transfersize; resid < length;
848c1b3d7c5SThomas E. Spanjaard 	     resid += sizeof(int16_t))
849c1b3d7c5SThomas E. Spanjaard 	    ATA_IDX_OUTW(ch, ATA_DATA, 0);
850c1b3d7c5SThomas E. Spanjaard     }
851c1b3d7c5SThomas E. Spanjaard }
852