xref: /dflybsd-src/sys/dev/disk/nata/ata-disk.c (revision 3ec9ecbcf37fc06ba7e36d70b8e76d3bf5d1ed32)
1c1b3d7c5SThomas E. Spanjaard /*-
2c1b3d7c5SThomas E. Spanjaard  * 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  *
2602d7aa4aSSascha Wildner  * $FreeBSD: src/sys/dev/ata/ata-disk.c,v 1.199 2006/09/14 19:12:29 sos Exp $
27*3ec9ecbcSMatthew Dillon  * $DragonFly: src/sys/dev/disk/nata/ata-disk.c,v 1.7 2008/03/24 06:41:56 dillon Exp $
28c1b3d7c5SThomas E. Spanjaard  */
29c1b3d7c5SThomas E. Spanjaard 
30c1b3d7c5SThomas E. Spanjaard #include "opt_ata.h"
31c1b3d7c5SThomas E. Spanjaard 
32c1b3d7c5SThomas E. Spanjaard #include <sys/param.h>
33c1b3d7c5SThomas E. Spanjaard #include <sys/bio.h>
34c1b3d7c5SThomas E. Spanjaard #include <sys/buf.h>
35c1b3d7c5SThomas E. Spanjaard #include <sys/bus.h>
36c1b3d7c5SThomas E. Spanjaard #include <sys/device.h>
37c1b3d7c5SThomas E. Spanjaard #include <sys/devicestat.h>
38c1b3d7c5SThomas E. Spanjaard #include <sys/disk.h>
39c1b3d7c5SThomas E. Spanjaard #include <sys/libkern.h>
40c1b3d7c5SThomas E. Spanjaard #include <sys/malloc.h>
41c1b3d7c5SThomas E. Spanjaard #include <sys/module.h>
42c1b3d7c5SThomas E. Spanjaard #include <sys/nata.h>
43c1b3d7c5SThomas E. Spanjaard #include <sys/systm.h>
44c1b3d7c5SThomas E. Spanjaard 
45c1b3d7c5SThomas E. Spanjaard #include <vm/pmap.h>
46c1b3d7c5SThomas E. Spanjaard 
47c1b3d7c5SThomas E. Spanjaard #include <machine/md_var.h>
48c1b3d7c5SThomas E. Spanjaard 
49c1b3d7c5SThomas E. Spanjaard #include "ata-all.h"
50c1b3d7c5SThomas E. Spanjaard #include "ata-disk.h"
51c1b3d7c5SThomas E. Spanjaard #include "ata_if.h"
52c1b3d7c5SThomas E. Spanjaard 
53c1b3d7c5SThomas E. Spanjaard /* device structure */
54c1b3d7c5SThomas E. Spanjaard static	d_open_t	ad_open;
55c1b3d7c5SThomas E. Spanjaard static	d_close_t	ad_close;
56c1b3d7c5SThomas E. Spanjaard static	d_ioctl_t	ad_ioctl;
57c1b3d7c5SThomas E. Spanjaard static	d_strategy_t	ad_strategy;
58c1b3d7c5SThomas E. Spanjaard static	d_dump_t	ad_dump;
59c1b3d7c5SThomas E. Spanjaard static struct dev_ops ad_ops = {
60c1b3d7c5SThomas E. Spanjaard 	{ "ad", 116, D_DISK },
61c1b3d7c5SThomas E. Spanjaard 	.d_open =	ad_open,
62c1b3d7c5SThomas E. Spanjaard 	.d_close =	ad_close,
63c1b3d7c5SThomas E. Spanjaard 	.d_read =	physread,
64c1b3d7c5SThomas E. Spanjaard 	.d_write =	physwrite,
65c1b3d7c5SThomas E. Spanjaard 	.d_ioctl =	ad_ioctl,
66c1b3d7c5SThomas E. Spanjaard 	.d_strategy =	ad_strategy,
67c1b3d7c5SThomas E. Spanjaard 	.d_dump =	ad_dump,
68c1b3d7c5SThomas E. Spanjaard };
69c1b3d7c5SThomas E. Spanjaard 
70c1b3d7c5SThomas E. Spanjaard /* prototypes */
71c1b3d7c5SThomas E. Spanjaard static void ad_init(device_t);
72c1b3d7c5SThomas E. Spanjaard static void ad_done(struct ata_request *);
73c1b3d7c5SThomas E. Spanjaard static void ad_describe(device_t dev);
74c1b3d7c5SThomas E. Spanjaard static int ad_version(u_int16_t);
75c1b3d7c5SThomas E. Spanjaard 
76c1b3d7c5SThomas E. Spanjaard /* local vars */
77c1b3d7c5SThomas E. Spanjaard static MALLOC_DEFINE(M_AD, "ad_driver", "ATA disk driver");
78c1b3d7c5SThomas E. Spanjaard 
79c1b3d7c5SThomas E. Spanjaard static int
80c1b3d7c5SThomas E. Spanjaard ad_probe(device_t dev)
81c1b3d7c5SThomas E. Spanjaard {
82c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
83c1b3d7c5SThomas E. Spanjaard 
84c1b3d7c5SThomas E. Spanjaard     if (!(atadev->param.config & ATA_PROTO_ATAPI) ||
85c1b3d7c5SThomas E. Spanjaard 	(atadev->param.config == ATA_CFA_MAGIC1) ||
86*3ec9ecbcSMatthew Dillon 	(atadev->param.config == ATA_CFA_MAGIC2) ||
87*3ec9ecbcSMatthew Dillon 	(atadev->param.config == ATA_CFA_MAGIC3))
88c1b3d7c5SThomas E. Spanjaard 	return 0;
89c1b3d7c5SThomas E. Spanjaard     else
90c1b3d7c5SThomas E. Spanjaard 	return ENXIO;
91c1b3d7c5SThomas E. Spanjaard }
92c1b3d7c5SThomas E. Spanjaard 
93c1b3d7c5SThomas E. Spanjaard static int
94c1b3d7c5SThomas E. Spanjaard ad_attach(device_t dev)
95c1b3d7c5SThomas E. Spanjaard {
96c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(device_get_parent(dev));
97c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
98a688b15cSMatthew Dillon     struct disk_info info;
99c1b3d7c5SThomas E. Spanjaard     struct ad_softc *adp;
100c1b3d7c5SThomas E. Spanjaard     cdev_t cdev;
101c1b3d7c5SThomas E. Spanjaard     u_int32_t lbasize;
102c1b3d7c5SThomas E. Spanjaard     u_int64_t lbasize48;
103c1b3d7c5SThomas E. Spanjaard 
104c1b3d7c5SThomas E. Spanjaard     /* check that we have a virgin disk to attach */
105c1b3d7c5SThomas E. Spanjaard     if (device_get_ivars(dev))
106c1b3d7c5SThomas E. Spanjaard 	return EEXIST;
107c1b3d7c5SThomas E. Spanjaard 
108a01741bbSMatthew Dillon     adp = kmalloc(sizeof(struct ad_softc), M_AD, M_INTWAIT | M_ZERO);
109c1b3d7c5SThomas E. Spanjaard     device_set_ivars(dev, adp);
110c1b3d7c5SThomas E. Spanjaard 
11187870bc8SMatthew Dillon     if ((atadev->param.atavalid & ATA_FLAG_54_58) &&
11287870bc8SMatthew Dillon 	atadev->param.current_heads && atadev->param.current_sectors) {
113c1b3d7c5SThomas E. Spanjaard 	adp->heads = atadev->param.current_heads;
114c1b3d7c5SThomas E. Spanjaard 	adp->sectors = atadev->param.current_sectors;
115c1b3d7c5SThomas E. Spanjaard 	adp->total_secs = (u_int32_t)atadev->param.current_size_1 |
116c1b3d7c5SThomas E. Spanjaard 			  ((u_int32_t)atadev->param.current_size_2 << 16);
117c1b3d7c5SThomas E. Spanjaard     }
118c1b3d7c5SThomas E. Spanjaard     else {
119c1b3d7c5SThomas E. Spanjaard 	adp->heads = atadev->param.heads;
120c1b3d7c5SThomas E. Spanjaard 	adp->sectors = atadev->param.sectors;
121c1b3d7c5SThomas E. Spanjaard 	adp->total_secs = atadev->param.cylinders * adp->heads * adp->sectors;
122c1b3d7c5SThomas E. Spanjaard     }
123c1b3d7c5SThomas E. Spanjaard     lbasize = (u_int32_t)atadev->param.lba_size_1 |
124c1b3d7c5SThomas E. Spanjaard 	      ((u_int32_t)atadev->param.lba_size_2 << 16);
125c1b3d7c5SThomas E. Spanjaard 
126c1b3d7c5SThomas E. Spanjaard     /* does this device need oldstyle CHS addressing */
127c1b3d7c5SThomas E. Spanjaard     if (!ad_version(atadev->param.version_major) || !lbasize)
128c1b3d7c5SThomas E. Spanjaard 	atadev->flags |= ATA_D_USE_CHS;
129c1b3d7c5SThomas E. Spanjaard 
130c1b3d7c5SThomas E. Spanjaard     /* use the 28bit LBA size if valid or bigger than the CHS mapping */
131c1b3d7c5SThomas E. Spanjaard     if (atadev->param.cylinders == 16383 || adp->total_secs < lbasize)
132c1b3d7c5SThomas E. Spanjaard 	adp->total_secs = lbasize;
133c1b3d7c5SThomas E. Spanjaard 
134c1b3d7c5SThomas E. Spanjaard     /* use the 48bit LBA size if valid */
135c1b3d7c5SThomas E. Spanjaard     lbasize48 = ((u_int64_t)atadev->param.lba_size48_1) |
136c1b3d7c5SThomas E. Spanjaard 		((u_int64_t)atadev->param.lba_size48_2 << 16) |
137c1b3d7c5SThomas E. Spanjaard 		((u_int64_t)atadev->param.lba_size48_3 << 32) |
138c1b3d7c5SThomas E. Spanjaard 		((u_int64_t)atadev->param.lba_size48_4 << 48);
139c1b3d7c5SThomas E. Spanjaard     if ((atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) &&
140c1b3d7c5SThomas E. Spanjaard 	lbasize48 > ATA_MAX_28BIT_LBA)
141c1b3d7c5SThomas E. Spanjaard 	adp->total_secs = lbasize48;
142c1b3d7c5SThomas E. Spanjaard 
143c1b3d7c5SThomas E. Spanjaard     /* init device parameters */
144c1b3d7c5SThomas E. Spanjaard     ad_init(dev);
145c1b3d7c5SThomas E. Spanjaard 
146c1b3d7c5SThomas E. Spanjaard     /* create the disk device */
147c1b3d7c5SThomas E. Spanjaard     /* XXX TGEN Maybe use DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_DIRECT,
148c1b3d7c5SThomas E. Spanjaard        DEVSTAT_PRIORITY_MAX. */
149c1b3d7c5SThomas E. Spanjaard     devstat_add_entry(&adp->stats, "ad", device_get_unit(dev), DEV_BSIZE,
150c1b3d7c5SThomas E. Spanjaard 		      DEVSTAT_NO_ORDERED_TAGS,
151c1b3d7c5SThomas E. Spanjaard 		      DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE,
152c1b3d7c5SThomas E. Spanjaard 		      DEVSTAT_PRIORITY_DISK);
153a688b15cSMatthew Dillon     cdev = disk_create(device_get_unit(dev), &adp->disk, &ad_ops);
154c1b3d7c5SThomas E. Spanjaard     cdev->si_drv1 = dev;
155c1b3d7c5SThomas E. Spanjaard     if (ch->dma)
156c1b3d7c5SThomas E. Spanjaard         cdev->si_iosize_max = ch->dma->max_iosize;
157c1b3d7c5SThomas E. Spanjaard     else
158c1b3d7c5SThomas E. Spanjaard         cdev->si_iosize_max = DFLTPHYS;
159c1b3d7c5SThomas E. Spanjaard     adp->cdev = cdev;
160a688b15cSMatthew Dillon 
161a688b15cSMatthew Dillon     bzero(&info, sizeof(info));
162a688b15cSMatthew Dillon     info.d_media_blksize = DEV_BSIZE;		/* mandatory */
163a688b15cSMatthew Dillon     info.d_media_blocks = adp->total_secs;
164a688b15cSMatthew Dillon 
165a688b15cSMatthew Dillon     info.d_secpertrack = adp->sectors;		/* optional */
166a688b15cSMatthew Dillon     info.d_nheads = adp->heads;
167a688b15cSMatthew Dillon     info.d_ncylinders = adp->total_secs/(adp->heads*adp->sectors);
168a688b15cSMatthew Dillon     info.d_secpercyl = adp->sectors * adp->heads;
169a688b15cSMatthew Dillon 
170c1b3d7c5SThomas E. Spanjaard     device_add_child(dev, "subdisk", device_get_unit(dev));
171c1b3d7c5SThomas E. Spanjaard     bus_generic_attach(dev);
172c1b3d7c5SThomas E. Spanjaard 
173c1b3d7c5SThomas E. Spanjaard     /* announce we are here */
174c1b3d7c5SThomas E. Spanjaard     ad_describe(dev);
175a688b15cSMatthew Dillon 
176a688b15cSMatthew Dillon     disk_setdiskinfo(&adp->disk, &info);
177a688b15cSMatthew Dillon 
178c1b3d7c5SThomas E. Spanjaard     return 0;
179c1b3d7c5SThomas E. Spanjaard }
180c1b3d7c5SThomas E. Spanjaard 
181c1b3d7c5SThomas E. Spanjaard static int
182c1b3d7c5SThomas E. Spanjaard ad_detach(device_t dev)
183c1b3d7c5SThomas E. Spanjaard {
184c1b3d7c5SThomas E. Spanjaard     struct ad_softc *adp = device_get_ivars(dev);
185c1b3d7c5SThomas E. Spanjaard     device_t *children;
186c1b3d7c5SThomas E. Spanjaard     int nchildren, i;
187c1b3d7c5SThomas E. Spanjaard 
188c1b3d7c5SThomas E. Spanjaard     /* check that we have a valid disk to detach */
189c1b3d7c5SThomas E. Spanjaard     if (!adp)
190c1b3d7c5SThomas E. Spanjaard 	return ENXIO;
191c1b3d7c5SThomas E. Spanjaard 
192c1b3d7c5SThomas E. Spanjaard #if 0 /* XXX TGEN Probably useless, we fail the queue below. */
193c1b3d7c5SThomas E. Spanjaard     /* check that the disk is closed */
194c1b3d7c5SThomas E. Spanjaard     if (adp->ad_flags & AD_DISK_OPEN)
195c1b3d7c5SThomas E. Spanjaard         return EBUSY;
196c1b3d7c5SThomas E. Spanjaard #endif /* 0 */
197c1b3d7c5SThomas E. Spanjaard 
198c1b3d7c5SThomas E. Spanjaard     /* detach & delete all children */
199c1b3d7c5SThomas E. Spanjaard     if (!device_get_children(dev, &children, &nchildren)) {
200c1b3d7c5SThomas E. Spanjaard 	for (i = 0; i < nchildren; i++)
201c1b3d7c5SThomas E. Spanjaard 	    if (children[i])
202c1b3d7c5SThomas E. Spanjaard 		device_delete_child(dev, children[i]);
203c1b3d7c5SThomas E. Spanjaard 	kfree(children, M_TEMP);
204c1b3d7c5SThomas E. Spanjaard     }
205c1b3d7c5SThomas E. Spanjaard 
206c1b3d7c5SThomas E. Spanjaard     /* detroy disk from the system so we dont get any further requests */
207c1b3d7c5SThomas E. Spanjaard     disk_invalidate(&adp->disk);
208c1b3d7c5SThomas E. Spanjaard     disk_destroy(&adp->disk);
209c1b3d7c5SThomas E. Spanjaard 
210c1b3d7c5SThomas E. Spanjaard     /* fail requests on the queue and any thats "in flight" for this device */
211c1b3d7c5SThomas E. Spanjaard     ata_fail_requests(dev);
212c1b3d7c5SThomas E. Spanjaard 
213c1b3d7c5SThomas E. Spanjaard     /* dont leave anything behind */
214c1b3d7c5SThomas E. Spanjaard     /* disk_destroy() already took care of the dev_ops */
215c1b3d7c5SThomas E. Spanjaard     devstat_remove_entry(&adp->stats);
216c1b3d7c5SThomas E. Spanjaard     device_set_ivars(dev, NULL);
217c1b3d7c5SThomas E. Spanjaard     kfree(adp, M_AD);
218c1b3d7c5SThomas E. Spanjaard     return 0;
219c1b3d7c5SThomas E. Spanjaard }
220c1b3d7c5SThomas E. Spanjaard 
221c1b3d7c5SThomas E. Spanjaard static void
222c1b3d7c5SThomas E. Spanjaard ad_shutdown(device_t dev)
223c1b3d7c5SThomas E. Spanjaard {
224c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
225c1b3d7c5SThomas E. Spanjaard 
226c1b3d7c5SThomas E. Spanjaard     if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
227c1b3d7c5SThomas E. Spanjaard 	ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
228c1b3d7c5SThomas E. Spanjaard }
229c1b3d7c5SThomas E. Spanjaard 
230c1b3d7c5SThomas E. Spanjaard static int
231c1b3d7c5SThomas E. Spanjaard ad_reinit(device_t dev)
232c1b3d7c5SThomas E. Spanjaard {
233c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(device_get_parent(dev));
234c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
235c1b3d7c5SThomas E. Spanjaard 
236c1b3d7c5SThomas E. Spanjaard     /* if detach pending, return error */
237c1b3d7c5SThomas E. Spanjaard     if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATA_MASTER)) ||
238c1b3d7c5SThomas E. Spanjaard 	((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATA_SLAVE))) {
239c1b3d7c5SThomas E. Spanjaard 	return 1;
240c1b3d7c5SThomas E. Spanjaard     }
241c1b3d7c5SThomas E. Spanjaard     ad_init(dev);
242c1b3d7c5SThomas E. Spanjaard     return 0;
243c1b3d7c5SThomas E. Spanjaard }
244c1b3d7c5SThomas E. Spanjaard 
245c1b3d7c5SThomas E. Spanjaard static int
246c1b3d7c5SThomas E. Spanjaard ad_open(struct dev_open_args *ap)
247c1b3d7c5SThomas E. Spanjaard {
248c1b3d7c5SThomas E. Spanjaard     device_t dev = ap->a_head.a_dev->si_drv1;
249c1b3d7c5SThomas E. Spanjaard     struct ad_softc *adp = device_get_ivars(dev);
250c1b3d7c5SThomas E. Spanjaard 
251c1b3d7c5SThomas E. Spanjaard     if (!adp || adp->cdev == NULL)
252c1b3d7c5SThomas E. Spanjaard 	return ENXIO;
253c1b3d7c5SThomas E. Spanjaard     if(!device_is_attached(dev))
254c1b3d7c5SThomas E. Spanjaard 	return EBUSY;
255c1b3d7c5SThomas E. Spanjaard 
256c1b3d7c5SThomas E. Spanjaard #if 0 /* XXX TGEN Probably useless, queue will be failed on detach. */
257c1b3d7c5SThomas E. Spanjaard     adp->ad_flags &= AD_DISK_OPEN;
258c1b3d7c5SThomas E. Spanjaard #endif /* 0 */
259c1b3d7c5SThomas E. Spanjaard     return 0;
260c1b3d7c5SThomas E. Spanjaard }
261c1b3d7c5SThomas E. Spanjaard 
262c1b3d7c5SThomas E. Spanjaard static int
263c1b3d7c5SThomas E. Spanjaard ad_close(struct dev_close_args *ap)
264c1b3d7c5SThomas E. Spanjaard {
26587870bc8SMatthew Dillon #if 0 /* XXX TGEN Probably useless, queue will be failed on detach. */
266c1b3d7c5SThomas E. Spanjaard     device_t dev = ap->a_head.a_dev->si_drv1;
267c1b3d7c5SThomas E. Spanjaard     struct ad_softc *adp = device_get_ivars(dev);
268c1b3d7c5SThomas E. Spanjaard     adp->ad_flags |= AD_DISK_OPEN;
269c1b3d7c5SThomas E. Spanjaard #endif /* 0 */
270c1b3d7c5SThomas E. Spanjaard     return 0;
271c1b3d7c5SThomas E. Spanjaard }
272c1b3d7c5SThomas E. Spanjaard 
273c1b3d7c5SThomas E. Spanjaard static int
274c1b3d7c5SThomas E. Spanjaard ad_ioctl(struct dev_ioctl_args *ap)
275c1b3d7c5SThomas E. Spanjaard {
276c1b3d7c5SThomas E. Spanjaard     return ata_device_ioctl(ap->a_head.a_dev->si_drv1, ap->a_cmd, ap->a_data);
277c1b3d7c5SThomas E. Spanjaard }
278c1b3d7c5SThomas E. Spanjaard 
279c1b3d7c5SThomas E. Spanjaard static int
280c1b3d7c5SThomas E. Spanjaard ad_strategy(struct dev_strategy_args *ap)
281c1b3d7c5SThomas E. Spanjaard {
282c1b3d7c5SThomas E. Spanjaard     device_t dev =  ap->a_head.a_dev->si_drv1;
283c1b3d7c5SThomas E. Spanjaard     struct bio *bp = ap->a_bio;
284c1b3d7c5SThomas E. Spanjaard     struct buf *bbp = bp->bio_buf;
285c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
286c1b3d7c5SThomas E. Spanjaard     struct ata_request *request;
287c1b3d7c5SThomas E. Spanjaard     struct ad_softc *adp = device_get_ivars(dev);
288c1b3d7c5SThomas E. Spanjaard 
289c1b3d7c5SThomas E. Spanjaard     if (!(request = ata_alloc_request())) {
290c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "FAILURE - out of memory in strategy\n");
291c1b3d7c5SThomas E. Spanjaard 	bbp->b_flags |= B_ERROR;
292c1b3d7c5SThomas E. Spanjaard 	bbp->b_error = ENOMEM;
293c1b3d7c5SThomas E. Spanjaard 	biodone(bp);
294c1b3d7c5SThomas E. Spanjaard 	return(0);
295c1b3d7c5SThomas E. Spanjaard     }
296c1b3d7c5SThomas E. Spanjaard 
297c1b3d7c5SThomas E. Spanjaard     /* setup request */
298c1b3d7c5SThomas E. Spanjaard     request->dev = dev;
299c1b3d7c5SThomas E. Spanjaard     request->bio = bp;
300c1b3d7c5SThomas E. Spanjaard     request->callback = ad_done;
301c1b3d7c5SThomas E. Spanjaard     request->timeout = 5;
302c1b3d7c5SThomas E. Spanjaard     request->retries = 2;
303c1b3d7c5SThomas E. Spanjaard     request->data = bbp->b_data;
304c1b3d7c5SThomas E. Spanjaard     request->bytecount = bbp->b_bcount;
305c1b3d7c5SThomas E. Spanjaard     /* lba is block granularity, convert byte granularity bio_offset */
306c1b3d7c5SThomas E. Spanjaard     request->u.ata.lba = (u_int64_t)(bp->bio_offset >> DEV_BSHIFT);
307c1b3d7c5SThomas E. Spanjaard     request->u.ata.count = request->bytecount / DEV_BSIZE;
308c1b3d7c5SThomas E. Spanjaard     request->transfersize = min(bbp->b_bcount, atadev->max_iosize);
309c1b3d7c5SThomas E. Spanjaard 
310c1b3d7c5SThomas E. Spanjaard     switch (bbp->b_cmd) {
311c1b3d7c5SThomas E. Spanjaard     case BUF_CMD_READ:
312c1b3d7c5SThomas E. Spanjaard 	request->flags = ATA_R_READ;
313c1b3d7c5SThomas E. Spanjaard 	if (atadev->mode >= ATA_DMA) {
314c1b3d7c5SThomas E. Spanjaard 	    request->u.ata.command = ATA_READ_DMA;
315c1b3d7c5SThomas E. Spanjaard 	    request->flags |= ATA_R_DMA;
316c1b3d7c5SThomas E. Spanjaard 	}
317c1b3d7c5SThomas E. Spanjaard 	else if (request->transfersize > DEV_BSIZE)
318c1b3d7c5SThomas E. Spanjaard 	    request->u.ata.command = ATA_READ_MUL;
319c1b3d7c5SThomas E. Spanjaard 	else
320c1b3d7c5SThomas E. Spanjaard 	    request->u.ata.command = ATA_READ;
321c1b3d7c5SThomas E. Spanjaard 	break;
322c1b3d7c5SThomas E. Spanjaard     case BUF_CMD_WRITE:
323c1b3d7c5SThomas E. Spanjaard 	request->flags = ATA_R_WRITE;
324c1b3d7c5SThomas E. Spanjaard 	if (atadev->mode >= ATA_DMA) {
325c1b3d7c5SThomas E. Spanjaard 	    request->u.ata.command = ATA_WRITE_DMA;
326c1b3d7c5SThomas E. Spanjaard 	    request->flags |= ATA_R_DMA;
327c1b3d7c5SThomas E. Spanjaard 	}
328c1b3d7c5SThomas E. Spanjaard 	else if (request->transfersize > DEV_BSIZE)
329c1b3d7c5SThomas E. Spanjaard 	    request->u.ata.command = ATA_WRITE_MUL;
330c1b3d7c5SThomas E. Spanjaard 	else
331c1b3d7c5SThomas E. Spanjaard 	    request->u.ata.command = ATA_WRITE;
332c1b3d7c5SThomas E. Spanjaard 	break;
33387870bc8SMatthew Dillon #if 0	/* NOT YET */
33487870bc8SMatthew Dillon     case BUF_CMD_FLUSH:
33587870bc8SMatthew Dillon 	request->u.ata.lba = 0;
33687870bc8SMatthew Dillon 	request->u.ata.count = 0;
33787870bc8SMatthew Dillon 	request->u.ata.feature = 0;
33887870bc8SMatthew Dillon 	request->bytecount = 0;
33987870bc8SMatthew Dillon 	request->transfersize = 0;
34087870bc8SMatthew Dillon 	request->flags = ATA_R_CONTROL;
34187870bc8SMatthew Dillon 	request->u.ata.command = ATA_FLUSHCACHE;
34287870bc8SMatthew Dillon 	break;
34387870bc8SMatthew Dillon #endif
344c1b3d7c5SThomas E. Spanjaard     default:
345c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "FAILURE - unknown BUF operation\n");
346c1b3d7c5SThomas E. Spanjaard 	ata_free_request(request);
347c1b3d7c5SThomas E. Spanjaard 	bbp->b_flags |= B_ERROR;
348c1b3d7c5SThomas E. Spanjaard 	bbp->b_error = EIO;
349c1b3d7c5SThomas E. Spanjaard 	biodone(bp);
350c1b3d7c5SThomas E. Spanjaard 	return(0);
351c1b3d7c5SThomas E. Spanjaard     }
352c1b3d7c5SThomas E. Spanjaard     request->flags |= ATA_R_ORDERED;
353c1b3d7c5SThomas E. Spanjaard     devstat_start_transaction(&adp->stats);
354c1b3d7c5SThomas E. Spanjaard     ata_queue_request(request);
355c1b3d7c5SThomas E. Spanjaard     return(0);
356c1b3d7c5SThomas E. Spanjaard }
357c1b3d7c5SThomas E. Spanjaard 
358c1b3d7c5SThomas E. Spanjaard static void
359c1b3d7c5SThomas E. Spanjaard ad_done(struct ata_request *request)
360c1b3d7c5SThomas E. Spanjaard {
361c1b3d7c5SThomas E. Spanjaard     struct ad_softc *adp = device_get_ivars(request->dev);
362c1b3d7c5SThomas E. Spanjaard     struct bio *bp = request->bio;
363c1b3d7c5SThomas E. Spanjaard     struct buf *bbp = bp->bio_buf;
364c1b3d7c5SThomas E. Spanjaard 
365c1b3d7c5SThomas E. Spanjaard     /* finish up transfer */
366c1b3d7c5SThomas E. Spanjaard     if ((bbp->b_error = request->result))
367c1b3d7c5SThomas E. Spanjaard 	bbp->b_flags |= B_ERROR;
368c1b3d7c5SThomas E. Spanjaard     bbp->b_resid = bbp->b_bcount - request->donecount;
369c1b3d7c5SThomas E. Spanjaard     devstat_end_transaction_buf(&adp->stats, bbp);
370c1b3d7c5SThomas E. Spanjaard     biodone(bp);
371c1b3d7c5SThomas E. Spanjaard     ata_free_request(request);
372c1b3d7c5SThomas E. Spanjaard }
373c1b3d7c5SThomas E. Spanjaard 
374c1b3d7c5SThomas E. Spanjaard static int
375c1b3d7c5SThomas E. Spanjaard ad_dump(struct dev_dump_args *ap)
376c1b3d7c5SThomas E. Spanjaard {
377c1b3d7c5SThomas E. Spanjaard     device_t dev = ap->a_head.a_dev->si_drv1;
378c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
3795c16e43eSThomas E. Spanjaard     struct ata_request request;
380c1b3d7c5SThomas E. Spanjaard     vm_paddr_t addr = 0;
381c1b3d7c5SThomas E. Spanjaard     long blkcnt;
382c1b3d7c5SThomas E. Spanjaard     int dumppages = MAXDUMPPGS;
383c1b3d7c5SThomas E. Spanjaard     int error = 0;
384c1b3d7c5SThomas E. Spanjaard     int i;
385c1b3d7c5SThomas E. Spanjaard 
386c1b3d7c5SThomas E. Spanjaard     blkcnt = howmany(PAGE_SIZE, ap->a_secsize);
387c1b3d7c5SThomas E. Spanjaard 
388c1b3d7c5SThomas E. Spanjaard     while (ap->a_count > 0) {
389c1b3d7c5SThomas E. Spanjaard 	caddr_t va = NULL;
390c1b3d7c5SThomas E. Spanjaard 
391c1b3d7c5SThomas E. Spanjaard 	if ((ap->a_count /blkcnt) < dumppages)
392c1b3d7c5SThomas E. Spanjaard 	    dumppages = ap->a_count / blkcnt;
393c1b3d7c5SThomas E. Spanjaard 
394c1b3d7c5SThomas E. Spanjaard 	for (i = 0; i < dumppages; ++i) {
395c1b3d7c5SThomas E. Spanjaard 	    vm_paddr_t a = addr + (i * PAGE_SIZE);
396c1b3d7c5SThomas E. Spanjaard 	    if (is_physical_memory(a))
397c1b3d7c5SThomas E. Spanjaard 		va = pmap_kenter_temporary(trunc_page(a), i);
398c1b3d7c5SThomas E. Spanjaard 	    else
399c1b3d7c5SThomas E. Spanjaard 		va = pmap_kenter_temporary(trunc_page(0), i);
400c1b3d7c5SThomas E. Spanjaard 	}
401c1b3d7c5SThomas E. Spanjaard 
4025c16e43eSThomas E. Spanjaard 	bzero(&request, sizeof(struct ata_request));
4035c16e43eSThomas E. Spanjaard 	request.dev = dev;
4045c16e43eSThomas E. Spanjaard 	/* request.bio = NULL; */
4055c16e43eSThomas E. Spanjaard 	request.timeout = 5;
4065c16e43eSThomas E. Spanjaard 	request.retries = 2;
4075c16e43eSThomas E. Spanjaard 	request.data = va;
4085c16e43eSThomas E. Spanjaard 	request.bytecount = dumppages * PAGE_SIZE;
4095c16e43eSThomas E. Spanjaard 	request.u.ata.lba = ap->a_blkno;
4105c16e43eSThomas E. Spanjaard 	request.u.ata.count = request.bytecount / DEV_BSIZE;
4115c16e43eSThomas E. Spanjaard 	request.transfersize = min(request.bytecount, atadev->max_iosize);
4125c16e43eSThomas E. Spanjaard 	request.flags = ATA_R_WRITE | ATA_R_AT_HEAD;
4135c16e43eSThomas E. Spanjaard 	if (atadev->mode >= ATA_DMA) {
4145c16e43eSThomas E. Spanjaard 	    request.u.ata.command = ATA_WRITE_DMA;
4155c16e43eSThomas E. Spanjaard 	    request.flags |= ATA_DMA;
4165c16e43eSThomas E. Spanjaard 	} else if (request.transfersize > DEV_BSIZE)
4175c16e43eSThomas E. Spanjaard 	    request.u.ata.command = ATA_WRITE_MUL;
4185c16e43eSThomas E. Spanjaard 	else
4195c16e43eSThomas E. Spanjaard 	    request.u.ata.command = ATA_WRITE;
4205c16e43eSThomas E. Spanjaard 	ata_queue_request(&request);
4215c16e43eSThomas E. Spanjaard 	if (request.result)
4225c16e43eSThomas E. Spanjaard 	    return request.result;
423c1b3d7c5SThomas E. Spanjaard 
424c1b3d7c5SThomas E. Spanjaard 	if (dumpstatus(addr, (off_t)ap->a_count * DEV_BSIZE) < 0)
425c1b3d7c5SThomas E. Spanjaard 	    return EINTR;
426c1b3d7c5SThomas E. Spanjaard 
427c1b3d7c5SThomas E. Spanjaard 	ap->a_blkno += blkcnt * dumppages;
428c1b3d7c5SThomas E. Spanjaard 	ap->a_count -= blkcnt * dumppages;
429c1b3d7c5SThomas E. Spanjaard 	addr += PAGE_SIZE * dumppages;
430c1b3d7c5SThomas E. Spanjaard     }
431c1b3d7c5SThomas E. Spanjaard 
432c1b3d7c5SThomas E. Spanjaard     /* flush buffers to media */
433c1b3d7c5SThomas E. Spanjaard     if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
434c1b3d7c5SThomas E. Spanjaard 	error = ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
435c1b3d7c5SThomas E. Spanjaard     return error;
436c1b3d7c5SThomas E. Spanjaard }
437c1b3d7c5SThomas E. Spanjaard 
438c1b3d7c5SThomas E. Spanjaard static void
439c1b3d7c5SThomas E. Spanjaard ad_init(device_t dev)
440c1b3d7c5SThomas E. Spanjaard {
441c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
442c1b3d7c5SThomas E. Spanjaard 
443c1b3d7c5SThomas E. Spanjaard     ATA_SETMODE(device_get_parent(dev), dev);
444c1b3d7c5SThomas E. Spanjaard 
445c1b3d7c5SThomas E. Spanjaard     /* enable readahead caching */
446c1b3d7c5SThomas E. Spanjaard     if (atadev->param.support.command1 & ATA_SUPPORT_LOOKAHEAD)
447c1b3d7c5SThomas E. Spanjaard 	ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0);
448c1b3d7c5SThomas E. Spanjaard 
449c1b3d7c5SThomas E. Spanjaard     /* enable write caching if supported and configured */
450c1b3d7c5SThomas E. Spanjaard     if (atadev->param.support.command1 & ATA_SUPPORT_WRITECACHE) {
451c1b3d7c5SThomas E. Spanjaard 	if (ata_wc)
452c1b3d7c5SThomas E. Spanjaard 	    ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0);
453c1b3d7c5SThomas E. Spanjaard 	else
454c1b3d7c5SThomas E. Spanjaard 	    ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0);
455c1b3d7c5SThomas E. Spanjaard     }
456c1b3d7c5SThomas E. Spanjaard 
457c1b3d7c5SThomas E. Spanjaard     /* use multiple sectors/interrupt if device supports it */
458c1b3d7c5SThomas E. Spanjaard     if (ad_version(atadev->param.version_major)) {
459c1b3d7c5SThomas E. Spanjaard 	int secsperint = max(1, min(atadev->param.sectors_intr, 16));
460c1b3d7c5SThomas E. Spanjaard 
461c1b3d7c5SThomas E. Spanjaard 	if (!ata_controlcmd(dev, ATA_SET_MULTI, 0, 0, secsperint))
462c1b3d7c5SThomas E. Spanjaard 	    atadev->max_iosize = secsperint * DEV_BSIZE;
463c1b3d7c5SThomas E. Spanjaard     }
464c1b3d7c5SThomas E. Spanjaard     else
465c1b3d7c5SThomas E. Spanjaard 	atadev->max_iosize = DEV_BSIZE;
466c1b3d7c5SThomas E. Spanjaard }
467c1b3d7c5SThomas E. Spanjaard 
468c1b3d7c5SThomas E. Spanjaard void
469c1b3d7c5SThomas E. Spanjaard ad_describe(device_t dev)
470c1b3d7c5SThomas E. Spanjaard {
471c1b3d7c5SThomas E. Spanjaard     struct ata_channel *ch = device_get_softc(device_get_parent(dev));
472c1b3d7c5SThomas E. Spanjaard     struct ata_device *atadev = device_get_softc(dev);
473c1b3d7c5SThomas E. Spanjaard     struct ad_softc *adp = device_get_ivars(dev);
474c1b3d7c5SThomas E. Spanjaard     u_int8_t *marker, vendor[64], product[64];
475c1b3d7c5SThomas E. Spanjaard 
476c1b3d7c5SThomas E. Spanjaard     /* try to seperate the ATA model string into vendor and model parts */
477c1b3d7c5SThomas E. Spanjaard     if ((marker = index(atadev->param.model, ' ')) ||
478c1b3d7c5SThomas E. Spanjaard 	(marker = index(atadev->param.model, '-'))) {
479c1b3d7c5SThomas E. Spanjaard 	int len = (marker - atadev->param.model);
480c1b3d7c5SThomas E. Spanjaard 
481c1b3d7c5SThomas E. Spanjaard 	strncpy(vendor, atadev->param.model, len);
482c1b3d7c5SThomas E. Spanjaard 	vendor[len++] = 0;
483c1b3d7c5SThomas E. Spanjaard 	strcat(vendor, " ");
484c1b3d7c5SThomas E. Spanjaard 	strncpy(product, atadev->param.model + len, 40 - len);
485c1b3d7c5SThomas E. Spanjaard 	vendor[40 - len] = 0;
486c1b3d7c5SThomas E. Spanjaard     }
487c1b3d7c5SThomas E. Spanjaard     else {
488c1b3d7c5SThomas E. Spanjaard 	if (!strncmp(atadev->param.model, "ST", 2))
489c1b3d7c5SThomas E. Spanjaard 	    strcpy(vendor, "Seagate ");
490*3ec9ecbcSMatthew Dillon 	else if (!strncmp(atadev->param.model, "HDS", 3))
491*3ec9ecbcSMatthew Dillon 	    strcpy(vendor, "Hitachi ");
492c1b3d7c5SThomas E. Spanjaard 	else
493c1b3d7c5SThomas E. Spanjaard 	    strcpy(vendor, "");
494c1b3d7c5SThomas E. Spanjaard 	strncpy(product, atadev->param.model, 40);
495c1b3d7c5SThomas E. Spanjaard     }
496c1b3d7c5SThomas E. Spanjaard 
497c1b3d7c5SThomas E. Spanjaard     device_printf(dev, "%lluMB <%s%s %.8s> at ata%d-%s %s%s\n",
498c1b3d7c5SThomas E. Spanjaard 		  (unsigned long long)(adp->total_secs / (1048576 / DEV_BSIZE)),
499c1b3d7c5SThomas E. Spanjaard 		  vendor, product, atadev->param.revision,
500c1b3d7c5SThomas E. Spanjaard 		  device_get_unit(ch->dev),
501c1b3d7c5SThomas E. Spanjaard 		  (atadev->unit == ATA_MASTER) ? "master" : "slave",
502c1b3d7c5SThomas E. Spanjaard 		  (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "",
503c1b3d7c5SThomas E. Spanjaard 		  ata_mode2str(atadev->mode));
504c1b3d7c5SThomas E. Spanjaard     if (bootverbose) {
505c1b3d7c5SThomas E. Spanjaard 	device_printf(dev, "%llu sectors [%lluC/%dH/%dS] "
506c1b3d7c5SThomas E. Spanjaard 		      "%d sectors/interrupt %d depth queue\n",
507c1b3d7c5SThomas E. Spanjaard 		      (unsigned long long)adp->total_secs,(unsigned long long)(
508c1b3d7c5SThomas E. Spanjaard 		      adp->total_secs / (adp->heads * adp->sectors)),
509c1b3d7c5SThomas E. Spanjaard 		      adp->heads, adp->sectors, atadev->max_iosize / DEV_BSIZE,
510c1b3d7c5SThomas E. Spanjaard 		      adp->num_tags + 1);
511c1b3d7c5SThomas E. Spanjaard     }
512c1b3d7c5SThomas E. Spanjaard }
513c1b3d7c5SThomas E. Spanjaard 
514c1b3d7c5SThomas E. Spanjaard static int
515c1b3d7c5SThomas E. Spanjaard ad_version(u_int16_t version)
516c1b3d7c5SThomas E. Spanjaard {
517c1b3d7c5SThomas E. Spanjaard     int bit;
518c1b3d7c5SThomas E. Spanjaard 
519c1b3d7c5SThomas E. Spanjaard     if (version == 0xffff)
520c1b3d7c5SThomas E. Spanjaard 	return 0;
521c1b3d7c5SThomas E. Spanjaard     for (bit = 15; bit >= 0; bit--)
522c1b3d7c5SThomas E. Spanjaard 	if (version & (1<<bit))
523c1b3d7c5SThomas E. Spanjaard 	    return bit;
524c1b3d7c5SThomas E. Spanjaard     return 0;
525c1b3d7c5SThomas E. Spanjaard }
526c1b3d7c5SThomas E. Spanjaard 
527c1b3d7c5SThomas E. Spanjaard static device_method_t ad_methods[] = {
528c1b3d7c5SThomas E. Spanjaard     /* device interface */
529c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(device_probe,     ad_probe),
530c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(device_attach,    ad_attach),
531c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(device_detach,    ad_detach),
532c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(device_shutdown,  ad_shutdown),
533c1b3d7c5SThomas E. Spanjaard 
534c1b3d7c5SThomas E. Spanjaard     /* ATA methods */
535c1b3d7c5SThomas E. Spanjaard     DEVMETHOD(ata_reinit,       ad_reinit),
536c1b3d7c5SThomas E. Spanjaard 
537c1b3d7c5SThomas E. Spanjaard     { 0, 0 }
538c1b3d7c5SThomas E. Spanjaard };
539c1b3d7c5SThomas E. Spanjaard 
540c1b3d7c5SThomas E. Spanjaard static driver_t ad_driver = {
541c1b3d7c5SThomas E. Spanjaard     "ad",
542c1b3d7c5SThomas E. Spanjaard     ad_methods,
543c1b3d7c5SThomas E. Spanjaard     0,
544c1b3d7c5SThomas E. Spanjaard };
545c1b3d7c5SThomas E. Spanjaard 
546c1b3d7c5SThomas E. Spanjaard devclass_t ad_devclass;
547c1b3d7c5SThomas E. Spanjaard 
548c1b3d7c5SThomas E. Spanjaard DRIVER_MODULE(ad, ata, ad_driver, ad_devclass, NULL, NULL);
549c1b3d7c5SThomas E. Spanjaard MODULE_VERSION(ad, 1);
550c1b3d7c5SThomas E. Spanjaard MODULE_DEPEND(ad, ata, 1, 1, 1);
551