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 *
2602d7aa4aSSascha Wildner * $FreeBSD: src/sys/dev/ata/ata-disk.c,v 1.199 2006/09/14 19:12:29 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/bio.h>
33c1b3d7c5SThomas E. Spanjaard #include <sys/buf.h>
34c1b3d7c5SThomas E. Spanjaard #include <sys/bus.h>
35c1b3d7c5SThomas E. Spanjaard #include <sys/device.h>
36c1b3d7c5SThomas E. Spanjaard #include <sys/devicestat.h>
37c1b3d7c5SThomas E. Spanjaard #include <sys/disk.h>
38c1b3d7c5SThomas E. Spanjaard #include <sys/libkern.h>
39c1b3d7c5SThomas E. Spanjaard #include <sys/malloc.h>
40c1b3d7c5SThomas E. Spanjaard #include <sys/module.h>
41c1b3d7c5SThomas E. Spanjaard #include <sys/nata.h>
42c1b3d7c5SThomas E. Spanjaard #include <sys/systm.h>
43c1b3d7c5SThomas E. Spanjaard
44c1b3d7c5SThomas E. Spanjaard #include <vm/pmap.h>
45c1b3d7c5SThomas E. Spanjaard
46c1b3d7c5SThomas E. Spanjaard #include <machine/md_var.h>
47c1b3d7c5SThomas E. Spanjaard
48c1b3d7c5SThomas E. Spanjaard #include "ata-all.h"
49c1b3d7c5SThomas E. Spanjaard #include "ata-disk.h"
50c1b3d7c5SThomas E. Spanjaard #include "ata_if.h"
51c1b3d7c5SThomas E. Spanjaard
52bb15467aSzrj /* local implementation, to trigger a warning */
53bb15467aSzrj static inline void
biofinish(struct bio * bp,struct bio * x __unused,int error)54bb15467aSzrj biofinish(struct bio *bp, struct bio *x __unused, int error)
55bb15467aSzrj {
56bb15467aSzrj struct buf *bbp = bp->bio_buf;
57bb15467aSzrj
58bb15467aSzrj bbp->b_flags |= B_ERROR;
59bb15467aSzrj bbp->b_error = error;
60bb15467aSzrj biodone(bp);
61bb15467aSzrj }
62bb15467aSzrj
63c1b3d7c5SThomas E. Spanjaard /* device structure */
64c1b3d7c5SThomas E. Spanjaard static d_open_t ad_open;
65c1b3d7c5SThomas E. Spanjaard static d_close_t ad_close;
66c1b3d7c5SThomas E. Spanjaard static d_ioctl_t ad_ioctl;
67c1b3d7c5SThomas E. Spanjaard static d_strategy_t ad_strategy;
68c1b3d7c5SThomas E. Spanjaard static d_dump_t ad_dump;
69c1b3d7c5SThomas E. Spanjaard static struct dev_ops ad_ops = {
7047f4bca5SSascha Wildner { "ad", 0, D_DISK },
71c1b3d7c5SThomas E. Spanjaard .d_open = ad_open,
72c1b3d7c5SThomas E. Spanjaard .d_close = ad_close,
73c1b3d7c5SThomas E. Spanjaard .d_read = physread,
74c1b3d7c5SThomas E. Spanjaard .d_write = physwrite,
75c1b3d7c5SThomas E. Spanjaard .d_ioctl = ad_ioctl,
76c1b3d7c5SThomas E. Spanjaard .d_strategy = ad_strategy,
77c1b3d7c5SThomas E. Spanjaard .d_dump = ad_dump,
78c1b3d7c5SThomas E. Spanjaard };
79c1b3d7c5SThomas E. Spanjaard
80c1b3d7c5SThomas E. Spanjaard /* prototypes */
81*cbf684e5Szrj static void ad_init(device_t dev);
82*cbf684e5Szrj static void ad_get_geometry(device_t dev);
83*cbf684e5Szrj static void ad_done(struct ata_request *request);
84c1b3d7c5SThomas E. Spanjaard static void ad_describe(device_t dev);
85*cbf684e5Szrj static int ad_version(u_int16_t version);
86c1b3d7c5SThomas E. Spanjaard
87c1b3d7c5SThomas E. Spanjaard /* local vars */
88c1b3d7c5SThomas E. Spanjaard static MALLOC_DEFINE(M_AD, "ad_driver", "ATA disk driver");
89c1b3d7c5SThomas E. Spanjaard
90c1b3d7c5SThomas E. Spanjaard static int
ad_probe(device_t dev)91c1b3d7c5SThomas E. Spanjaard ad_probe(device_t dev)
92c1b3d7c5SThomas E. Spanjaard {
93c1b3d7c5SThomas E. Spanjaard struct ata_device *atadev = device_get_softc(dev);
94c1b3d7c5SThomas E. Spanjaard
95c1b3d7c5SThomas E. Spanjaard if (!(atadev->param.config & ATA_PROTO_ATAPI) ||
96c1b3d7c5SThomas E. Spanjaard (atadev->param.config == ATA_CFA_MAGIC1) ||
973ec9ecbcSMatthew Dillon (atadev->param.config == ATA_CFA_MAGIC2) ||
983ec9ecbcSMatthew Dillon (atadev->param.config == ATA_CFA_MAGIC3))
99c1b3d7c5SThomas E. Spanjaard return 0;
100c1b3d7c5SThomas E. Spanjaard else
101c1b3d7c5SThomas E. Spanjaard return ENXIO;
102c1b3d7c5SThomas E. Spanjaard }
103c1b3d7c5SThomas E. Spanjaard
104c1b3d7c5SThomas E. Spanjaard static int
ad_attach(device_t dev)105c1b3d7c5SThomas E. Spanjaard ad_attach(device_t dev)
106c1b3d7c5SThomas E. Spanjaard {
107c1b3d7c5SThomas E. Spanjaard struct ata_channel *ch = device_get_softc(device_get_parent(dev));
108c1b3d7c5SThomas E. Spanjaard struct ata_device *atadev = device_get_softc(dev);
109a688b15cSMatthew Dillon struct disk_info info;
110c1b3d7c5SThomas E. Spanjaard struct ad_softc *adp;
111c1b3d7c5SThomas E. Spanjaard cdev_t cdev;
112c1b3d7c5SThomas E. Spanjaard
113c1b3d7c5SThomas E. Spanjaard /* check that we have a virgin disk to attach */
114c1b3d7c5SThomas E. Spanjaard if (device_get_ivars(dev))
115c1b3d7c5SThomas E. Spanjaard return EEXIST;
116c1b3d7c5SThomas E. Spanjaard
117a01741bbSMatthew Dillon adp = kmalloc(sizeof(struct ad_softc), M_AD, M_INTWAIT | M_ZERO);
118c1b3d7c5SThomas E. Spanjaard device_set_ivars(dev, adp);
119c1b3d7c5SThomas E. Spanjaard
120*cbf684e5Szrj /* get device geometry into internal structs */
121*cbf684e5Szrj ad_get_geometry(dev);
122c1b3d7c5SThomas E. Spanjaard
123c1b3d7c5SThomas E. Spanjaard /* init device parameters */
124c1b3d7c5SThomas E. Spanjaard ad_init(dev);
125c1b3d7c5SThomas E. Spanjaard
126c1b3d7c5SThomas E. Spanjaard /* create the disk device */
127c1b3d7c5SThomas E. Spanjaard /* XXX TGEN Maybe use DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_DIRECT,
128c1b3d7c5SThomas E. Spanjaard DEVSTAT_PRIORITY_MAX. */
129c1b3d7c5SThomas E. Spanjaard devstat_add_entry(&adp->stats, "ad", device_get_unit(dev), DEV_BSIZE,
130c1b3d7c5SThomas E. Spanjaard DEVSTAT_NO_ORDERED_TAGS,
131c1b3d7c5SThomas E. Spanjaard DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE,
132c1b3d7c5SThomas E. Spanjaard DEVSTAT_PRIORITY_DISK);
133a688b15cSMatthew Dillon cdev = disk_create(device_get_unit(dev), &adp->disk, &ad_ops);
134c1b3d7c5SThomas E. Spanjaard cdev->si_drv1 = dev;
135c1b3d7c5SThomas E. Spanjaard if (ch->dma)
136c1b3d7c5SThomas E. Spanjaard cdev->si_iosize_max = ch->dma->max_iosize;
137c1b3d7c5SThomas E. Spanjaard else
138d83666e0SFrançois Tigeot cdev->si_iosize_max = min(MAXPHYS,64*1024);
139c1b3d7c5SThomas E. Spanjaard adp->cdev = cdev;
140a688b15cSMatthew Dillon
141a688b15cSMatthew Dillon bzero(&info, sizeof(info));
142a688b15cSMatthew Dillon info.d_media_blksize = DEV_BSIZE; /* mandatory */
143a688b15cSMatthew Dillon info.d_media_blocks = adp->total_secs;
144a688b15cSMatthew Dillon
145a688b15cSMatthew Dillon info.d_secpertrack = adp->sectors; /* optional */
146a688b15cSMatthew Dillon info.d_nheads = adp->heads;
147a688b15cSMatthew Dillon info.d_ncylinders = adp->total_secs/(adp->heads*adp->sectors);
148a688b15cSMatthew Dillon info.d_secpercyl = adp->sectors * adp->heads;
14955230951SMatthew Dillon info.d_serialno = atadev->param.serial;
150a688b15cSMatthew Dillon
151c1b3d7c5SThomas E. Spanjaard device_add_child(dev, "subdisk", device_get_unit(dev));
152c1b3d7c5SThomas E. Spanjaard bus_generic_attach(dev);
153c1b3d7c5SThomas E. Spanjaard
154c1b3d7c5SThomas E. Spanjaard /* announce we are here */
155c1b3d7c5SThomas E. Spanjaard ad_describe(dev);
156a688b15cSMatthew Dillon
157a688b15cSMatthew Dillon disk_setdiskinfo(&adp->disk, &info);
158a688b15cSMatthew Dillon
1599243051bSzrj #if defined(__DragonFly__)
1609243051bSzrj callout_init_mp(&atadev->spindown_timer);
1619243051bSzrj #else
1629243051bSzrj callout_init(&atadev->spindown_timer, 1);
1639243051bSzrj #endif
164c1b3d7c5SThomas E. Spanjaard return 0;
165c1b3d7c5SThomas E. Spanjaard }
166c1b3d7c5SThomas E. Spanjaard
167c1b3d7c5SThomas E. Spanjaard static int
ad_detach(device_t dev)168c1b3d7c5SThomas E. Spanjaard ad_detach(device_t dev)
169c1b3d7c5SThomas E. Spanjaard {
170c1b3d7c5SThomas E. Spanjaard struct ad_softc *adp = device_get_ivars(dev);
1719243051bSzrj struct ata_device *atadev = device_get_softc(dev);
172c1b3d7c5SThomas E. Spanjaard device_t *children;
173c1b3d7c5SThomas E. Spanjaard int nchildren, i;
174c1b3d7c5SThomas E. Spanjaard
175c1b3d7c5SThomas E. Spanjaard /* check that we have a valid disk to detach */
176c1b3d7c5SThomas E. Spanjaard if (!adp)
177c1b3d7c5SThomas E. Spanjaard return ENXIO;
178c1b3d7c5SThomas E. Spanjaard
1799243051bSzrj /* destroy the power timeout */
1809243051bSzrj callout_drain(&atadev->spindown_timer);
181c1b3d7c5SThomas E. Spanjaard
182c1b3d7c5SThomas E. Spanjaard /* detach & delete all children */
183c1b3d7c5SThomas E. Spanjaard if (!device_get_children(dev, &children, &nchildren)) {
184c1b3d7c5SThomas E. Spanjaard for (i = 0; i < nchildren; i++)
185c1b3d7c5SThomas E. Spanjaard if (children[i])
186c1b3d7c5SThomas E. Spanjaard device_delete_child(dev, children[i]);
187c1b3d7c5SThomas E. Spanjaard kfree(children, M_TEMP);
188c1b3d7c5SThomas E. Spanjaard }
189c1b3d7c5SThomas E. Spanjaard
190c1b3d7c5SThomas E. Spanjaard /* detroy disk from the system so we dont get any further requests */
191c1b3d7c5SThomas E. Spanjaard disk_invalidate(&adp->disk);
192c1b3d7c5SThomas E. Spanjaard disk_destroy(&adp->disk);
193c1b3d7c5SThomas E. Spanjaard
194c1b3d7c5SThomas E. Spanjaard /* fail requests on the queue and any thats "in flight" for this device */
195c1b3d7c5SThomas E. Spanjaard ata_fail_requests(dev);
196c1b3d7c5SThomas E. Spanjaard
197c1b3d7c5SThomas E. Spanjaard /* dont leave anything behind */
198c1b3d7c5SThomas E. Spanjaard /* disk_destroy() already took care of the dev_ops */
199c1b3d7c5SThomas E. Spanjaard devstat_remove_entry(&adp->stats);
200c1b3d7c5SThomas E. Spanjaard device_set_ivars(dev, NULL);
201c1b3d7c5SThomas E. Spanjaard kfree(adp, M_AD);
202c1b3d7c5SThomas E. Spanjaard return 0;
203c1b3d7c5SThomas E. Spanjaard }
204c1b3d7c5SThomas E. Spanjaard
205c1b3d7c5SThomas E. Spanjaard static void
ad_shutdown(device_t dev)206c1b3d7c5SThomas E. Spanjaard ad_shutdown(device_t dev)
207c1b3d7c5SThomas E. Spanjaard {
208c1b3d7c5SThomas E. Spanjaard struct ata_device *atadev = device_get_softc(dev);
209c1b3d7c5SThomas E. Spanjaard
210c1b3d7c5SThomas E. Spanjaard if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
211c1b3d7c5SThomas E. Spanjaard ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
212c1b3d7c5SThomas E. Spanjaard }
213c1b3d7c5SThomas E. Spanjaard
214c1b3d7c5SThomas E. Spanjaard static int
ad_reinit(device_t dev)215c1b3d7c5SThomas E. Spanjaard ad_reinit(device_t dev)
216c1b3d7c5SThomas E. Spanjaard {
217c1b3d7c5SThomas E. Spanjaard struct ata_channel *ch = device_get_softc(device_get_parent(dev));
218c1b3d7c5SThomas E. Spanjaard struct ata_device *atadev = device_get_softc(dev);
219c1b3d7c5SThomas E. Spanjaard
220c1b3d7c5SThomas E. Spanjaard /* if detach pending, return error */
221c1b3d7c5SThomas E. Spanjaard if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATA_MASTER)) ||
222c1b3d7c5SThomas E. Spanjaard ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATA_SLAVE))) {
223c1b3d7c5SThomas E. Spanjaard return 1;
224c1b3d7c5SThomas E. Spanjaard }
225c1b3d7c5SThomas E. Spanjaard ad_init(dev);
226c1b3d7c5SThomas E. Spanjaard return 0;
227c1b3d7c5SThomas E. Spanjaard }
228c1b3d7c5SThomas E. Spanjaard
2299243051bSzrj static void
ad_power_callback(struct ata_request * request)2309243051bSzrj ad_power_callback(struct ata_request *request)
2319243051bSzrj {
2329243051bSzrj device_printf(request->dev, "drive spun down.\n");
2339243051bSzrj ata_free_request(request);
2349243051bSzrj }
2359243051bSzrj
2369243051bSzrj static void
ad_spindown(void * priv)2379243051bSzrj ad_spindown(void *priv)
2389243051bSzrj {
2399243051bSzrj device_t dev = priv;
2409243051bSzrj struct ata_device *atadev = device_get_softc(dev);
2419243051bSzrj struct ata_request *request;
2429243051bSzrj
2439243051bSzrj if (!atadev->spindown)
2449243051bSzrj return;
2459243051bSzrj device_printf(dev, "Idle, spin down\n");
2469243051bSzrj atadev->spindown_state = 1;
2479243051bSzrj if (!(request = ata_alloc_request())) {
2489243051bSzrj device_printf(dev, "FAILURE - out of memory in ad_spindown\n");
2499243051bSzrj return;
2509243051bSzrj }
2519243051bSzrj request->dev = dev;
2529243051bSzrj request->flags = ATA_R_CONTROL;
2539243051bSzrj request->timeout = ATA_DEFAULT_TIMEOUT;
2549243051bSzrj request->retries = 1;
2559243051bSzrj request->callback = ad_power_callback;
2569243051bSzrj request->u.ata.command = ATA_STANDBY_IMMEDIATE;
2579243051bSzrj ata_queue_request(request);
2589243051bSzrj }
2599243051bSzrj
260c1b3d7c5SThomas E. Spanjaard static int
ad_open(struct dev_open_args * ap)261c1b3d7c5SThomas E. Spanjaard ad_open(struct dev_open_args *ap)
262c1b3d7c5SThomas E. Spanjaard {
263c1b3d7c5SThomas E. Spanjaard device_t dev = ap->a_head.a_dev->si_drv1;
264c1b3d7c5SThomas E. Spanjaard struct ad_softc *adp = device_get_ivars(dev);
265c1b3d7c5SThomas E. Spanjaard
266c1b3d7c5SThomas E. Spanjaard if (!adp || adp->cdev == NULL)
267c1b3d7c5SThomas E. Spanjaard return ENXIO;
268c1b3d7c5SThomas E. Spanjaard if(!device_is_attached(dev))
269c1b3d7c5SThomas E. Spanjaard return EBUSY;
270c1b3d7c5SThomas E. Spanjaard
271c1b3d7c5SThomas E. Spanjaard return 0;
272c1b3d7c5SThomas E. Spanjaard }
273c1b3d7c5SThomas E. Spanjaard
274c1b3d7c5SThomas E. Spanjaard static int
ad_close(struct dev_close_args * ap)275c1b3d7c5SThomas E. Spanjaard ad_close(struct dev_close_args *ap)
276c1b3d7c5SThomas E. Spanjaard {
277c1b3d7c5SThomas E. Spanjaard return 0;
278c1b3d7c5SThomas E. Spanjaard }
279c1b3d7c5SThomas E. Spanjaard
280c1b3d7c5SThomas E. Spanjaard static int
ad_strategy(struct dev_strategy_args * ap)281c1b3d7c5SThomas E. Spanjaard ad_strategy(struct dev_strategy_args *ap)
282c1b3d7c5SThomas E. Spanjaard {
283c1b3d7c5SThomas E. Spanjaard device_t dev = ap->a_head.a_dev->si_drv1;
284c1b3d7c5SThomas E. Spanjaard struct bio *bp = ap->a_bio;
285c1b3d7c5SThomas E. Spanjaard struct buf *bbp = bp->bio_buf;
286c1b3d7c5SThomas E. Spanjaard struct ata_device *atadev = device_get_softc(dev);
287c1b3d7c5SThomas E. Spanjaard struct ata_request *request;
288c1b3d7c5SThomas E. Spanjaard struct ad_softc *adp = device_get_ivars(dev);
289c1b3d7c5SThomas E. Spanjaard
2909243051bSzrj if (atadev->spindown)
2919243051bSzrj callout_reset(&atadev->spindown_timer, hz * atadev->spindown,
2929243051bSzrj ad_spindown, dev);
2939243051bSzrj
294c1b3d7c5SThomas E. Spanjaard if (!(request = ata_alloc_request())) {
295c1b3d7c5SThomas E. Spanjaard device_printf(dev, "FAILURE - out of memory in strategy\n");
296bb15467aSzrj biofinish(bp, NULL, ENOMEM);
297c1b3d7c5SThomas E. Spanjaard return(0);
298c1b3d7c5SThomas E. Spanjaard }
299c1b3d7c5SThomas E. Spanjaard
300c1b3d7c5SThomas E. Spanjaard /* setup request */
301c1b3d7c5SThomas E. Spanjaard request->dev = dev;
302c1b3d7c5SThomas E. Spanjaard request->bio = bp;
303c1b3d7c5SThomas E. Spanjaard request->callback = ad_done;
3049243051bSzrj if (atadev->spindown_state) {
3059243051bSzrj device_printf(dev, "request while spun down, starting.\n");
3069243051bSzrj atadev->spindown_state = 0;
3079243051bSzrj request->timeout = MAX(ATA_DEFAULT_TIMEOUT, 31);
3089243051bSzrj } else {
3093c0963e0SMatthew Dillon request->timeout = ATA_DEFAULT_TIMEOUT;
3109243051bSzrj }
311c1b3d7c5SThomas E. Spanjaard request->retries = 2;
312c1b3d7c5SThomas E. Spanjaard request->data = bbp->b_data;
313c1b3d7c5SThomas E. Spanjaard request->bytecount = bbp->b_bcount;
314c1b3d7c5SThomas E. Spanjaard /* lba is block granularity, convert byte granularity bio_offset */
315c1b3d7c5SThomas E. Spanjaard request->u.ata.lba = (u_int64_t)(bp->bio_offset >> DEV_BSHIFT);
316c1b3d7c5SThomas E. Spanjaard request->u.ata.count = request->bytecount / DEV_BSIZE;
317c1b3d7c5SThomas E. Spanjaard request->transfersize = min(bbp->b_bcount, atadev->max_iosize);
318c1b3d7c5SThomas E. Spanjaard
319c1b3d7c5SThomas E. Spanjaard switch (bbp->b_cmd) {
320c1b3d7c5SThomas E. Spanjaard case BUF_CMD_READ:
321c1b3d7c5SThomas E. Spanjaard request->flags = ATA_R_READ;
322c1b3d7c5SThomas E. Spanjaard if (atadev->mode >= ATA_DMA) {
323c1b3d7c5SThomas E. Spanjaard request->u.ata.command = ATA_READ_DMA;
324c1b3d7c5SThomas E. Spanjaard request->flags |= ATA_R_DMA;
325c1b3d7c5SThomas E. Spanjaard }
326c1b3d7c5SThomas E. Spanjaard else if (request->transfersize > DEV_BSIZE)
327c1b3d7c5SThomas E. Spanjaard request->u.ata.command = ATA_READ_MUL;
328c1b3d7c5SThomas E. Spanjaard else
329c1b3d7c5SThomas E. Spanjaard request->u.ata.command = ATA_READ;
330c1b3d7c5SThomas E. Spanjaard break;
331c1b3d7c5SThomas E. Spanjaard case BUF_CMD_WRITE:
332c1b3d7c5SThomas E. Spanjaard request->flags = ATA_R_WRITE;
333c1b3d7c5SThomas E. Spanjaard if (atadev->mode >= ATA_DMA) {
334c1b3d7c5SThomas E. Spanjaard request->u.ata.command = ATA_WRITE_DMA;
335c1b3d7c5SThomas E. Spanjaard request->flags |= ATA_R_DMA;
336c1b3d7c5SThomas E. Spanjaard }
337c1b3d7c5SThomas E. Spanjaard else if (request->transfersize > DEV_BSIZE)
338c1b3d7c5SThomas E. Spanjaard request->u.ata.command = ATA_WRITE_MUL;
339c1b3d7c5SThomas E. Spanjaard else
340c1b3d7c5SThomas E. Spanjaard request->u.ata.command = ATA_WRITE;
341c1b3d7c5SThomas E. Spanjaard break;
34287870bc8SMatthew Dillon case BUF_CMD_FLUSH:
34387870bc8SMatthew Dillon request->u.ata.lba = 0;
34487870bc8SMatthew Dillon request->u.ata.count = 0;
34587870bc8SMatthew Dillon request->u.ata.feature = 0;
34687870bc8SMatthew Dillon request->bytecount = 0;
34787870bc8SMatthew Dillon request->transfersize = 0;
34887870bc8SMatthew Dillon request->flags = ATA_R_CONTROL;
34987870bc8SMatthew Dillon request->u.ata.command = ATA_FLUSHCACHE;
3507648d8d6SVenkatesh Srinivas /* ATA FLUSHCACHE requests may take up to 30 sec to timeout */
351*cbf684e5Szrj request->timeout = 30; /* ATA_DEFAULT_TIMEOUT */
35287870bc8SMatthew Dillon break;
353c1b3d7c5SThomas E. Spanjaard default:
354c1b3d7c5SThomas E. Spanjaard device_printf(dev, "FAILURE - unknown BUF operation\n");
355c1b3d7c5SThomas E. Spanjaard ata_free_request(request);
356bb15467aSzrj biofinish(bp, NULL, EIO);
357c1b3d7c5SThomas E. Spanjaard return(0);
358c1b3d7c5SThomas E. Spanjaard }
359c1b3d7c5SThomas E. Spanjaard request->flags |= ATA_R_ORDERED;
360c1b3d7c5SThomas E. Spanjaard devstat_start_transaction(&adp->stats);
361c1b3d7c5SThomas E. Spanjaard ata_queue_request(request);
362c1b3d7c5SThomas E. Spanjaard return(0);
363c1b3d7c5SThomas E. Spanjaard }
364c1b3d7c5SThomas E. Spanjaard
365c1b3d7c5SThomas E. Spanjaard static void
ad_done(struct ata_request * request)366c1b3d7c5SThomas E. Spanjaard ad_done(struct ata_request *request)
367c1b3d7c5SThomas E. Spanjaard {
368c1b3d7c5SThomas E. Spanjaard struct ad_softc *adp = device_get_ivars(request->dev);
369c1b3d7c5SThomas E. Spanjaard struct bio *bp = request->bio;
370c1b3d7c5SThomas E. Spanjaard struct buf *bbp = bp->bio_buf;
371c1b3d7c5SThomas E. Spanjaard
372c1b3d7c5SThomas E. Spanjaard /* finish up transfer */
373c1b3d7c5SThomas E. Spanjaard if ((bbp->b_error = request->result))
374c1b3d7c5SThomas E. Spanjaard bbp->b_flags |= B_ERROR;
375c1b3d7c5SThomas E. Spanjaard bbp->b_resid = bbp->b_bcount - request->donecount;
376c1b3d7c5SThomas E. Spanjaard devstat_end_transaction_buf(&adp->stats, bbp);
377c1b3d7c5SThomas E. Spanjaard biodone(bp);
378c1b3d7c5SThomas E. Spanjaard ata_free_request(request);
379c1b3d7c5SThomas E. Spanjaard }
380c1b3d7c5SThomas E. Spanjaard
381c1b3d7c5SThomas E. Spanjaard static int
ad_ioctl(struct dev_ioctl_args * ap)382bb15467aSzrj ad_ioctl(struct dev_ioctl_args *ap)
383bb15467aSzrj {
384bb15467aSzrj return ata_device_ioctl(ap->a_head.a_dev->si_drv1, ap->a_cmd, ap->a_data);
385bb15467aSzrj }
386bb15467aSzrj
387bb15467aSzrj static int
ad_dump(struct dev_dump_args * ap)388c1b3d7c5SThomas E. Spanjaard ad_dump(struct dev_dump_args *ap)
389c1b3d7c5SThomas E. Spanjaard {
390c1b3d7c5SThomas E. Spanjaard device_t dev = ap->a_head.a_dev->si_drv1;
391c1b3d7c5SThomas E. Spanjaard struct ata_device *atadev = device_get_softc(dev);
3925c16e43eSThomas E. Spanjaard struct ata_request request;
393c1b3d7c5SThomas E. Spanjaard
394ef352403SAlex Hornung ata_drop_requests(dev);
395b24cd69cSAlex Hornung /*
396b24cd69cSAlex Hornung * 0 length means flush buffers and return
397b24cd69cSAlex Hornung */
398b24cd69cSAlex Hornung if (ap->a_length == 0) {
399b24cd69cSAlex Hornung /* flush buffers to media */
400b24cd69cSAlex Hornung if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
401b24cd69cSAlex Hornung return ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
402c1b3d7c5SThomas E. Spanjaard else
403b24cd69cSAlex Hornung return ENXIO;
404c1b3d7c5SThomas E. Spanjaard }
405c1b3d7c5SThomas E. Spanjaard
4065c16e43eSThomas E. Spanjaard bzero(&request, sizeof(struct ata_request));
4075c16e43eSThomas E. Spanjaard request.dev = dev;
408b24cd69cSAlex Hornung
409b24cd69cSAlex Hornung request.data = ap->a_virtual;
410b24cd69cSAlex Hornung request.bytecount = ap->a_length;
4115c16e43eSThomas E. Spanjaard request.transfersize = min(request.bytecount, atadev->max_iosize);
412b24cd69cSAlex Hornung request.flags = ATA_R_WRITE;
413b24cd69cSAlex Hornung
4145c16e43eSThomas E. Spanjaard if (atadev->mode >= ATA_DMA) {
4155c16e43eSThomas E. Spanjaard request.u.ata.command = ATA_WRITE_DMA;
4165c16e43eSThomas E. Spanjaard request.flags |= ATA_DMA;
4175c16e43eSThomas E. Spanjaard } else if (request.transfersize > DEV_BSIZE)
4185c16e43eSThomas E. Spanjaard request.u.ata.command = ATA_WRITE_MUL;
4195c16e43eSThomas E. Spanjaard else
4205c16e43eSThomas E. Spanjaard request.u.ata.command = ATA_WRITE;
421b24cd69cSAlex Hornung request.u.ata.lba = ap->a_offset / DEV_BSIZE;
422b24cd69cSAlex Hornung request.u.ata.count = request.bytecount / DEV_BSIZE;
423b24cd69cSAlex Hornung
424b24cd69cSAlex Hornung request.timeout = ATA_DEFAULT_TIMEOUT;
425b24cd69cSAlex Hornung request.retries = 2;
426b24cd69cSAlex Hornung
4275c16e43eSThomas E. Spanjaard ata_queue_request(&request);
4285c16e43eSThomas E. Spanjaard return request.result;
429c1b3d7c5SThomas E. Spanjaard }
430c1b3d7c5SThomas E. Spanjaard
431c1b3d7c5SThomas E. Spanjaard static void
ad_init(device_t dev)432c1b3d7c5SThomas E. Spanjaard ad_init(device_t dev)
433c1b3d7c5SThomas E. Spanjaard {
434c1b3d7c5SThomas E. Spanjaard struct ata_device *atadev = device_get_softc(dev);
435c1b3d7c5SThomas E. Spanjaard
436c1b3d7c5SThomas E. Spanjaard ATA_SETMODE(device_get_parent(dev), dev);
437c1b3d7c5SThomas E. Spanjaard
438c1b3d7c5SThomas E. Spanjaard /* enable readahead caching */
439c1b3d7c5SThomas E. Spanjaard if (atadev->param.support.command1 & ATA_SUPPORT_LOOKAHEAD)
440c1b3d7c5SThomas E. Spanjaard ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0);
441c1b3d7c5SThomas E. Spanjaard
442c1b3d7c5SThomas E. Spanjaard /* enable write caching if supported and configured */
443c1b3d7c5SThomas E. Spanjaard if (atadev->param.support.command1 & ATA_SUPPORT_WRITECACHE) {
444c1b3d7c5SThomas E. Spanjaard if (ata_wc)
445c1b3d7c5SThomas E. Spanjaard ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0);
446c1b3d7c5SThomas E. Spanjaard else
447c1b3d7c5SThomas E. Spanjaard ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0);
448c1b3d7c5SThomas E. Spanjaard }
449c1b3d7c5SThomas E. Spanjaard
450c1b3d7c5SThomas E. Spanjaard /* use multiple sectors/interrupt if device supports it */
451c1b3d7c5SThomas E. Spanjaard if (ad_version(atadev->param.version_major)) {
45279db9382SMatthew Dillon int secsperint = max(1, min(atadev->param.sectors_intr & 0xff, 16));
453c1b3d7c5SThomas E. Spanjaard
454c1b3d7c5SThomas E. Spanjaard if (!ata_controlcmd(dev, ATA_SET_MULTI, 0, 0, secsperint))
455c1b3d7c5SThomas E. Spanjaard atadev->max_iosize = secsperint * DEV_BSIZE;
456c1b3d7c5SThomas E. Spanjaard }
457c1b3d7c5SThomas E. Spanjaard else
458c1b3d7c5SThomas E. Spanjaard atadev->max_iosize = DEV_BSIZE;
459c1b3d7c5SThomas E. Spanjaard }
460c1b3d7c5SThomas E. Spanjaard
4618406cf70SSascha Wildner static void
ad_get_geometry(device_t dev)462*cbf684e5Szrj ad_get_geometry(device_t dev)
463*cbf684e5Szrj {
464*cbf684e5Szrj struct ata_device *atadev = device_get_softc(dev);
465*cbf684e5Szrj struct ad_softc *adp = device_get_ivars(dev);
466*cbf684e5Szrj u_int64_t lbasize48;
467*cbf684e5Szrj u_int32_t lbasize;
468*cbf684e5Szrj
469*cbf684e5Szrj if ((atadev->param.atavalid & ATA_FLAG_54_58) &&
470*cbf684e5Szrj atadev->param.current_heads && atadev->param.current_sectors) {
471*cbf684e5Szrj adp->heads = atadev->param.current_heads;
472*cbf684e5Szrj adp->sectors = atadev->param.current_sectors;
473*cbf684e5Szrj adp->total_secs = (u_int32_t)atadev->param.current_size_1 |
474*cbf684e5Szrj ((u_int32_t)atadev->param.current_size_2 << 16);
475*cbf684e5Szrj }
476*cbf684e5Szrj else {
477*cbf684e5Szrj adp->heads = atadev->param.heads;
478*cbf684e5Szrj adp->sectors = atadev->param.sectors;
479*cbf684e5Szrj adp->total_secs = atadev->param.cylinders * adp->heads * adp->sectors;
480*cbf684e5Szrj }
481*cbf684e5Szrj lbasize = (u_int32_t)atadev->param.lba_size_1 |
482*cbf684e5Szrj ((u_int32_t)atadev->param.lba_size_2 << 16);
483*cbf684e5Szrj
484*cbf684e5Szrj /* does this device need oldstyle CHS addressing */
485*cbf684e5Szrj if (!ad_version(atadev->param.version_major) || !lbasize)
486*cbf684e5Szrj atadev->flags |= ATA_D_USE_CHS;
487*cbf684e5Szrj
488*cbf684e5Szrj /* use the 28bit LBA size if valid or bigger than the CHS mapping */
489*cbf684e5Szrj if (atadev->param.cylinders == 16383 || adp->total_secs < lbasize)
490*cbf684e5Szrj adp->total_secs = lbasize;
491*cbf684e5Szrj
492*cbf684e5Szrj /* use the 48bit LBA size if valid */
493*cbf684e5Szrj lbasize48 = ((u_int64_t)atadev->param.lba_size48_1) |
494*cbf684e5Szrj ((u_int64_t)atadev->param.lba_size48_2 << 16) |
495*cbf684e5Szrj ((u_int64_t)atadev->param.lba_size48_3 << 32) |
496*cbf684e5Szrj ((u_int64_t)atadev->param.lba_size48_4 << 48);
497*cbf684e5Szrj if ((atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) &&
498*cbf684e5Szrj lbasize48 > ATA_MAX_28BIT_LBA)
499*cbf684e5Szrj adp->total_secs = lbasize48;
500*cbf684e5Szrj }
501*cbf684e5Szrj
502*cbf684e5Szrj static void
ad_describe(device_t dev)503c1b3d7c5SThomas E. Spanjaard ad_describe(device_t dev)
504c1b3d7c5SThomas E. Spanjaard {
505c1b3d7c5SThomas E. Spanjaard struct ata_channel *ch = device_get_softc(device_get_parent(dev));
506c1b3d7c5SThomas E. Spanjaard struct ata_device *atadev = device_get_softc(dev);
507c1b3d7c5SThomas E. Spanjaard struct ad_softc *adp = device_get_ivars(dev);
508c1b3d7c5SThomas E. Spanjaard u_int8_t *marker, vendor[64], product[64];
509c1b3d7c5SThomas E. Spanjaard
510c1b3d7c5SThomas E. Spanjaard /* try to seperate the ATA model string into vendor and model parts */
511c1b3d7c5SThomas E. Spanjaard if ((marker = index(atadev->param.model, ' ')) ||
512c1b3d7c5SThomas E. Spanjaard (marker = index(atadev->param.model, '-'))) {
513c1b3d7c5SThomas E. Spanjaard int len = (marker - atadev->param.model);
514c1b3d7c5SThomas E. Spanjaard
515c1b3d7c5SThomas E. Spanjaard strncpy(vendor, atadev->param.model, len);
516c1b3d7c5SThomas E. Spanjaard vendor[len++] = 0;
517c1b3d7c5SThomas E. Spanjaard strcat(vendor, " ");
518c1b3d7c5SThomas E. Spanjaard strncpy(product, atadev->param.model + len, 40 - len);
519c1b3d7c5SThomas E. Spanjaard vendor[40 - len] = 0;
520c1b3d7c5SThomas E. Spanjaard }
521c1b3d7c5SThomas E. Spanjaard else {
522c1b3d7c5SThomas E. Spanjaard if (!strncmp(atadev->param.model, "ST", 2))
523c1b3d7c5SThomas E. Spanjaard strcpy(vendor, "Seagate ");
5243ec9ecbcSMatthew Dillon else if (!strncmp(atadev->param.model, "HDS", 3))
5253ec9ecbcSMatthew Dillon strcpy(vendor, "Hitachi ");
526c1b3d7c5SThomas E. Spanjaard else
527c1b3d7c5SThomas E. Spanjaard strcpy(vendor, "");
528c1b3d7c5SThomas E. Spanjaard strncpy(product, atadev->param.model, 40);
529c1b3d7c5SThomas E. Spanjaard }
530c1b3d7c5SThomas E. Spanjaard
531bb15467aSzrj device_printf(dev, "%juMB <%s%s %.8s> at ata%d-%s %s%s\n",
532bb15467aSzrj adp->total_secs / (1048576 / DEV_BSIZE),
533c1b3d7c5SThomas E. Spanjaard vendor, product, atadev->param.revision,
534*cbf684e5Szrj device_get_unit(ch->dev), ata_unit2str(atadev),
535c1b3d7c5SThomas E. Spanjaard (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "",
536c1b3d7c5SThomas E. Spanjaard ata_mode2str(atadev->mode));
537c1b3d7c5SThomas E. Spanjaard if (bootverbose) {
538bb15467aSzrj device_printf(dev, "%ju sectors [%juC/%dH/%dS] "
539bb15467aSzrj "%d sectors/interrupt %d depth queue\n", adp->total_secs,
540bb15467aSzrj adp->total_secs / (adp->heads * adp->sectors),
541c1b3d7c5SThomas E. Spanjaard adp->heads, adp->sectors, atadev->max_iosize / DEV_BSIZE,
542c1b3d7c5SThomas E. Spanjaard adp->num_tags + 1);
543c1b3d7c5SThomas E. Spanjaard }
544c1b3d7c5SThomas E. Spanjaard }
545c1b3d7c5SThomas E. Spanjaard
546c1b3d7c5SThomas E. Spanjaard static int
ad_version(u_int16_t version)547c1b3d7c5SThomas E. Spanjaard ad_version(u_int16_t version)
548c1b3d7c5SThomas E. Spanjaard {
549c1b3d7c5SThomas E. Spanjaard int bit;
550c1b3d7c5SThomas E. Spanjaard
551c1b3d7c5SThomas E. Spanjaard if (version == 0xffff)
552c1b3d7c5SThomas E. Spanjaard return 0;
553c1b3d7c5SThomas E. Spanjaard for (bit = 15; bit >= 0; bit--)
554c1b3d7c5SThomas E. Spanjaard if (version & (1<<bit))
555c1b3d7c5SThomas E. Spanjaard return bit;
556c1b3d7c5SThomas E. Spanjaard return 0;
557c1b3d7c5SThomas E. Spanjaard }
558c1b3d7c5SThomas E. Spanjaard
559c1b3d7c5SThomas E. Spanjaard static device_method_t ad_methods[] = {
560c1b3d7c5SThomas E. Spanjaard /* device interface */
561c1b3d7c5SThomas E. Spanjaard DEVMETHOD(device_probe, ad_probe),
562c1b3d7c5SThomas E. Spanjaard DEVMETHOD(device_attach, ad_attach),
563c1b3d7c5SThomas E. Spanjaard DEVMETHOD(device_detach, ad_detach),
564c1b3d7c5SThomas E. Spanjaard DEVMETHOD(device_shutdown, ad_shutdown),
565c1b3d7c5SThomas E. Spanjaard
566c1b3d7c5SThomas E. Spanjaard /* ATA methods */
567c1b3d7c5SThomas E. Spanjaard DEVMETHOD(ata_reinit, ad_reinit),
568c1b3d7c5SThomas E. Spanjaard
569d3c9c58eSSascha Wildner DEVMETHOD_END
570c1b3d7c5SThomas E. Spanjaard };
571c1b3d7c5SThomas E. Spanjaard
572c1b3d7c5SThomas E. Spanjaard static driver_t ad_driver = {
573c1b3d7c5SThomas E. Spanjaard "ad",
574c1b3d7c5SThomas E. Spanjaard ad_methods,
575c1b3d7c5SThomas E. Spanjaard 0,
576c1b3d7c5SThomas E. Spanjaard };
577c1b3d7c5SThomas E. Spanjaard
578c1b3d7c5SThomas E. Spanjaard devclass_t ad_devclass;
579c1b3d7c5SThomas E. Spanjaard
580c1b3d7c5SThomas E. Spanjaard DRIVER_MODULE(ad, ata, ad_driver, ad_devclass, NULL, NULL);
581c1b3d7c5SThomas E. Spanjaard MODULE_VERSION(ad, 1);
582c1b3d7c5SThomas E. Spanjaard MODULE_DEPEND(ad, ata, 1, 1, 1);
583